base
This commit is contained in:
@@ -0,0 +1,473 @@
|
||||
#include "FileHash.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define PARTITIAL_LIMIT (1) // (단위:MByte))
|
||||
typedef unsigned long long UINT64;
|
||||
|
||||
|
||||
CFileHash::CFileHash()
|
||||
{
|
||||
MDInit( &m_contextForBuffer );
|
||||
m_nPartitialSize = PARTITIAL_LIMIT*(1024*1024);
|
||||
}
|
||||
|
||||
string CFileHash::GetLastErrorMessage()
|
||||
{
|
||||
string szMessage = m_szLastErrorMessage;
|
||||
return szMessage;
|
||||
}
|
||||
|
||||
int CFileHash::GetPartitialSize()
|
||||
{
|
||||
return m_nPartitialSize/(1024*1024);
|
||||
}
|
||||
|
||||
bool CFileHash::SetPartitialSize( int nPartitialSize )
|
||||
{
|
||||
if( nPartitialSize <= 0)
|
||||
return false;
|
||||
m_nPartitialSize = nPartitialSize*(1024*1024);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFileHash::UpdateBufferHash(unsigned char* szBuffer, int nLen )
|
||||
{
|
||||
MDUpdate( &m_contextForBuffer, szBuffer, nLen );
|
||||
}
|
||||
|
||||
void CFileHash::GetBufferHash(string& szResult)
|
||||
{
|
||||
unsigned char szDigest[16];
|
||||
char szHexBuffer[3];
|
||||
int i;
|
||||
|
||||
szResult.clear();
|
||||
// 멤버 context를 이해애서 Digest값을 얻어온다.
|
||||
MDFinal( szDigest, &m_contextForBuffer );
|
||||
// 해시값을 hex_digest 값으로 바꿔준다.
|
||||
for( i = 0; i<16 ; i++ )
|
||||
{
|
||||
memset( szHexBuffer, 0x00, sizeof( szHexBuffer ) );
|
||||
sprintf( szHexBuffer, "%02x", szDigest[i] );
|
||||
szResult.append( (char*)szHexBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// @breif Buffer Hash 했던 값을 초기화 하는 함수.
|
||||
void CFileHash::ClearBufferHash()
|
||||
{
|
||||
MDInit( &m_contextForBuffer );
|
||||
}
|
||||
|
||||
/// @brief Sender에서 사용하는 함수로서 파일에 대한 해쉬값을 레벨에 따라 생성한다.
|
||||
/// @param szFileFullPath [in] 파일의 절대경로 값.
|
||||
/// @param nHashLevel [in] 해싱 방법에 대한 레벨 값.
|
||||
/// @param szResult [out] 해싱된 결과값을 되돌려주는 변수.
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool CFileHash::GetFileHash(const string& szFileFullPath, int nHashLevel, string& szResult)
|
||||
{
|
||||
struct stat filestatus;
|
||||
|
||||
switch(nHashLevel)
|
||||
{
|
||||
case HASH_LEVEL_NONE:
|
||||
szResult = "0000";
|
||||
break;
|
||||
case HASH_LEVEL_FULL:
|
||||
// 해시 대상파일 오픈.
|
||||
if( !GetHashFull(szFileFullPath, szResult) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case HASH_LEVEL_PARTITIAL:
|
||||
// 파일 사이즈를 체크한다. PARTITIAL_LIMIT 사이즈 이하이면
|
||||
stat( szFileFullPath.c_str(), &filestatus );
|
||||
if( filestatus.st_size <= m_nPartitialSize*2 )
|
||||
{
|
||||
// 2MB이하일경우 파일전체 해시값을 구한다.
|
||||
if( !GetHashFull(szFileFullPath, szResult) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}else
|
||||
{
|
||||
// 옵션일 경우 PARTITIAL_LIMIT사이즈의 1/2을
|
||||
// 파일 맨앞과 맨뒤에서 추출해서 해시값을 생성한다.
|
||||
if( !GetHashPartitial(szFileFullPath, szResult) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HASH_LEVEL_FRONT_PARTITIAL:
|
||||
// 파일 사이즈를 체크한다. 설정된 값 이하이면
|
||||
stat( szFileFullPath.c_str(), &filestatus );
|
||||
if( filestatus.st_size <= m_nPartitialSize )
|
||||
{
|
||||
// 파일전체 해시값을 구한다.
|
||||
if( !GetHashFull(szFileFullPath, szResult) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 파일 맨앞에서 해시값을 생성한다.
|
||||
if( !GetHashFrontPartitial(szFileFullPath, szResult) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
sprintf( m_szLastErrorMessage, "Not support level[%d] on this class", nHashLevel);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Receiver에서 사용하는 함수로서 해당파일의 해시값과 Sender에서 받은 해싱값을 비교한 결과를 전달한다.
|
||||
/// @param szFileFullPath [in] 파일의 절대경로 값.
|
||||
/// @param nHashLevel [in] 해싱 방법에 대한 레벨 값.
|
||||
/// @param szResult [out] Sender로 부터 받은 해시 값.
|
||||
/// @return 같은 값일경우 return true, 그 외에는 return false
|
||||
bool CFileHash::GetHashFull(const string& szFileFullPath, string& szResult)
|
||||
{
|
||||
int fd;
|
||||
int nLen;
|
||||
int i;
|
||||
MD_CTX context;
|
||||
unsigned char szBuffer[1024], szDigest[16];
|
||||
char szHexBuffer[3];
|
||||
|
||||
memset(szBuffer, 0x00, sizeof(szBuffer));
|
||||
memset(szDigest, 0x00, sizeof(szDigest));
|
||||
memset(szHexBuffer, 0x00, sizeof(szHexBuffer));
|
||||
szResult.clear();
|
||||
|
||||
if( ( fd = open(szFileFullPath.c_str(), O_RDONLY ) ) == -1 )
|
||||
{
|
||||
sprintf( m_szLastErrorMessage, "File open failed.[%s] ", szFileFullPath.c_str());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MDInit( &context );
|
||||
|
||||
while( ( nLen = read(fd, szBuffer, 1024) ) )
|
||||
MDUpdate( &context, szBuffer, nLen );
|
||||
|
||||
MDFinal( szDigest, &context );
|
||||
|
||||
// 해시값을 hex_digest 값으로 바꿔준다.
|
||||
for( i = 0; i<16 ; i++ )
|
||||
{
|
||||
memset( szHexBuffer, 0x00, sizeof( szHexBuffer ) );
|
||||
sprintf( szHexBuffer, "%02x", szDigest[i] );
|
||||
szResult.append( (char*)szHexBuffer);
|
||||
}
|
||||
close( fd );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CFileHash::GetHashPartitial(const string &szFileFullPath, string& szResult)
|
||||
{
|
||||
int fd;
|
||||
MD_CTX context;
|
||||
int nLen;
|
||||
int i;
|
||||
unsigned char szBuffer[1024], szDigest[16];
|
||||
char szHexBuffer[3];
|
||||
UINT64 nFilePos;
|
||||
|
||||
memset(szBuffer, 0x00, sizeof(szBuffer));
|
||||
memset(szDigest, 0x00, sizeof(szDigest));
|
||||
memset(szHexBuffer, 0x00, sizeof(szHexBuffer));
|
||||
szResult.clear();
|
||||
|
||||
if( ( fd = open(szFileFullPath.c_str(), O_RDONLY ) ) == -1 )
|
||||
{
|
||||
sprintf( m_szLastErrorMessage, "File open failed.[%s] ", szFileFullPath.c_str());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MDInit( &context );
|
||||
// 파일의 앞부분의 해시값을 추출한다.
|
||||
nFilePos = 0;
|
||||
|
||||
while( (nLen = read(fd, szBuffer, 1024)) )
|
||||
{
|
||||
MDUpdate( &context, szBuffer, nLen );
|
||||
nFilePos = lseek(fd, 0, SEEK_CUR);
|
||||
// 앞부분의 해시할 값까지만 하기위한 조건.
|
||||
if( (nFilePos >= (UINT64)(m_nPartitialSize)) )
|
||||
break;
|
||||
}
|
||||
|
||||
// 파일의 끝에서 해시값 추출할 시작점으로 파일포지션을 옮겨준다.
|
||||
lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, -m_nPartitialSize , SEEK_END);
|
||||
|
||||
while( (nLen = read(fd, szBuffer, 1024)) )
|
||||
{
|
||||
MDUpdate( &context, szBuffer, nLen );
|
||||
nFilePos = lseek(fd, 0, SEEK_CUR);
|
||||
}
|
||||
MDFinal( szDigest, &context );
|
||||
|
||||
// 해시값을 hex_digest 값으로 바꿔준다.
|
||||
for( i = 0; i<16 ; i++ )
|
||||
{
|
||||
memset( szHexBuffer, 0x00, sizeof( szHexBuffer ) );
|
||||
sprintf( szHexBuffer, "%02x", szDigest[i] );
|
||||
szResult.append( (char*)szHexBuffer);
|
||||
}
|
||||
close( fd );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CFileHash::GetHashFrontPartitial(const string &szFileFullPath, string& szResult)
|
||||
{
|
||||
int fd;
|
||||
MD_CTX context;
|
||||
int nLen;
|
||||
int i;
|
||||
unsigned char szBuffer[1024], szDigest[16];
|
||||
char szHexBuffer[3];
|
||||
UINT64 nFilePos;
|
||||
|
||||
memset(szBuffer, 0x00, sizeof(szBuffer));
|
||||
memset(szDigest, 0x00, sizeof(szDigest));
|
||||
memset(szHexBuffer, 0x00, sizeof(szHexBuffer));
|
||||
szResult.clear();
|
||||
|
||||
if( ( fd = open(szFileFullPath.c_str(), O_RDONLY ) ) == -1 )
|
||||
{
|
||||
sprintf( m_szLastErrorMessage, "File open failed.[%s] ", szFileFullPath.c_str());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MDInit( &context );
|
||||
// 파일의 앞부분의 해시값을 추출한다.
|
||||
nFilePos = 0;
|
||||
|
||||
while( (nLen = read(fd, szBuffer, 1024)) )
|
||||
{
|
||||
MDUpdate( &context, szBuffer, nLen );
|
||||
nFilePos = lseek(fd, 0, SEEK_CUR);
|
||||
// 앞부분의 해시할 값까지만 하기위한 조건.
|
||||
if( (nFilePos >= (UINT64)(m_nPartitialSize)) )
|
||||
break;
|
||||
}
|
||||
|
||||
MDFinal( szDigest, &context );
|
||||
|
||||
// 해시값을 hex_digest 값으로 바꿔준다.
|
||||
for( i = 0; i<16 ; i++ )
|
||||
{
|
||||
memset( szHexBuffer, 0x00, sizeof( szHexBuffer ) );
|
||||
sprintf( szHexBuffer, "%02x", szDigest[i] );
|
||||
szResult.append( (char*)szHexBuffer);
|
||||
}
|
||||
close( fd );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CFileHash::CheckFileHash(const string& szFileFullPath, int nHashLevel, const string szCompareHash)
|
||||
{
|
||||
string szMd5;
|
||||
|
||||
if( !GetFileHash(szFileFullPath, nHashLevel, szMd5 ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( szCompareHash.compare(szMd5) != 0 )
|
||||
{
|
||||
sprintf( m_szLastErrorMessage, "Hash not match. source[%s] local[%s]", szCompareHash.c_str(), szMd5.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460, #27533)
|
||||
/// @brief 토토디스크 고객사 지원용 파일 Hash 정보 추출 함수
|
||||
/// @param szFileFullPath [in] 파일의 절대경로 값.
|
||||
/// @param nFileTotalSize [in] 파일 크기 값.
|
||||
/// @param szResultTotosoraMd5 [out] TOTOROSA MD5 추출 결과값을 반환하기 위한 변수.
|
||||
/// @param szResultDnaCheckKey [out] 토토 DNA Check Key 추출 결과값을 반환하기 위한 변수.
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool CFileHash::GetHashTOTO( const string &szFileFullPath, const unsigned long long nFileTotalSize, string& szResultTotosoraMd5, string& szResultDnaCheckKey )
|
||||
{
|
||||
int fd;
|
||||
MD_CTX context;
|
||||
|
||||
int i;
|
||||
unsigned char szBuffer[10240], szDigest[16];
|
||||
char szHexBuffer[3];
|
||||
|
||||
memset( szBuffer, 0x00, sizeof( szBuffer ) );
|
||||
memset( szDigest, 0x00, sizeof( szDigest ) );
|
||||
memset( szHexBuffer, 0x00, sizeof( szHexBuffer ) );
|
||||
|
||||
szResultTotosoraMd5.clear();
|
||||
szResultDnaCheckKey.clear();
|
||||
|
||||
if( (fd = open( szFileFullPath.c_str(), O_RDONLY )) == -1 )
|
||||
{
|
||||
sprintf( m_szLastErrorMessage, "File open failed. [%s] ", szFileFullPath.c_str() );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////// TOTOROSA MD5 해시 추출 Start ///////////////////////
|
||||
// 2016-05-04 huibong ( #27460 )
|
||||
// 토토디스크 고객사 지원을 위해
|
||||
// 토토디스크에서 사용하는 Content Hash 추출 방법에 따른 기능 개발
|
||||
// 추출 관련해서는 MD5 Hash 추출 방법을 사용하며...
|
||||
// 파일 앞 최대 약 1MByte 까지 추출하고... 파일 크기가 1 M 이상이면.. 맨뒤의 10 KByte 추가 추출
|
||||
// 하지만.. 고객사 Java 코드 보니까.. 추출 코드가 전반적으로 이상하게 되어 있어....
|
||||
// 임의 수정시 문제 될 수 있으므로... 고객사에서 사용한 추출 Flow 를 그대로 사용한다.
|
||||
|
||||
|
||||
// 관련 변수 초기화.
|
||||
long nFileSize = 0;
|
||||
int dwBytesRead = 0;
|
||||
|
||||
bool bRun = true;
|
||||
|
||||
// MD5 hash 객체 초기화.
|
||||
MDInit( &context );
|
||||
|
||||
// 최초 10,240 만큼 read.
|
||||
dwBytesRead = read( fd, szBuffer, 10240 );
|
||||
|
||||
while( dwBytesRead > 0 )
|
||||
{
|
||||
nFileSize += dwBytesRead;
|
||||
MDUpdate( &context, szBuffer, dwBytesRead );
|
||||
|
||||
if( nFileSize > 1048576 )
|
||||
{
|
||||
if( (nFileTotalSize > 1048576 + 10240) && bRun )
|
||||
{
|
||||
// 파일의 끝에서 해시값 추출할 시작점으로 파일포지션을 옮겨준다.
|
||||
lseek( fd, 0, SEEK_END );
|
||||
lseek( fd, -10240, SEEK_END );
|
||||
|
||||
bRun = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dwBytesRead = read( fd, szBuffer, 10240 );
|
||||
}
|
||||
|
||||
MDFinal( szDigest, &context );
|
||||
|
||||
// 해시값을 hex_digest 값으로 바꿔준다.
|
||||
for( i = 0; i<16; i++ )
|
||||
{
|
||||
memset( szHexBuffer, 0x00, sizeof( szHexBuffer ) );
|
||||
sprintf( szHexBuffer, "%02x", szDigest[i] );
|
||||
szResultTotosoraMd5.append( (char*)szHexBuffer );
|
||||
}
|
||||
////////////// TOTOROSA MD5 해시 추출 End ///////////////////////
|
||||
|
||||
|
||||
// 파일 Position 을 맨 앞으로 이동 처리한다.
|
||||
lseek( fd, 0, SEEK_SET );
|
||||
|
||||
// 사용 버퍼 객체 초기화.
|
||||
memset( szBuffer, 0x00, sizeof( szBuffer ) );
|
||||
memset( szDigest, 0x00, sizeof( szDigest ) );
|
||||
memset( szHexBuffer, 0x00, sizeof( szHexBuffer ) );
|
||||
|
||||
|
||||
|
||||
////////////// DNA CHECK Key 추출 Start ///////////////////////
|
||||
// 2016-05-10 huibong ( #27533 )
|
||||
// 토토디스크 고객사 지원을 위해 토토디스크에서 사용하는 Content DNA 추출 방법에 따른 기능 개발
|
||||
// 추출 관련해서는 MD5 Hash 추출 방법을 사용하며...
|
||||
// 파일 앞 최대 약 10MByte 까지 추출한 후 Hash 생성 후.. 마지막에 파일 Size 정보를 append 처리하는 방식 사용
|
||||
// 하지만.. 고객사 Java 코드 보니까.. 코드가 전반적으로 이상하게 되어 있어만...
|
||||
// 임의 수정시 문제 될 수 있으므로... 고객사에서 사용한 추출 Flow 를 그대로 사용한다.
|
||||
|
||||
// MD5 hash 객체 초기화.
|
||||
MDInit( &context );
|
||||
|
||||
nFileSize = 10485760; // 10MB
|
||||
if( nFileTotalSize <= 10485760 )
|
||||
{
|
||||
nFileSize = nFileTotalSize;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if( nFileSize < 1024 )
|
||||
{
|
||||
dwBytesRead = read( fd, szBuffer, (int)nFileSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
dwBytesRead = read( fd, szBuffer, 1024 );
|
||||
}
|
||||
|
||||
MDUpdate( &context, szBuffer, dwBytesRead );
|
||||
nFileSize = nFileSize - dwBytesRead;
|
||||
|
||||
} while( nFileSize > 0 );
|
||||
|
||||
MDFinal( szDigest, &context );
|
||||
|
||||
// 해시값을 hex_digest 값으로 바꿔준다.
|
||||
for( i = 0; i<16; i++ )
|
||||
{
|
||||
memset( szHexBuffer, 0x00, sizeof( szHexBuffer ) );
|
||||
sprintf( szHexBuffer, "%02x", szDigest[i] );
|
||||
szResultDnaCheckKey.append( (char*)szHexBuffer );
|
||||
}
|
||||
|
||||
// 추출된 10M Hash 결과에 파일 Size 정보를 append 처리한다.
|
||||
memset( szBuffer, 0x00, sizeof( szBuffer ) );
|
||||
sprintf( (char*)szBuffer, "%llu", nFileTotalSize );
|
||||
|
||||
szResultDnaCheckKey.append( (char *)szBuffer );
|
||||
|
||||
|
||||
////////////// DNA CHECK Key 추출 End ///////////////////////
|
||||
|
||||
|
||||
// 파일 close
|
||||
close( fd );
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/***************************************************************************
|
||||
File Hash 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 __FILEHASH_H__
|
||||
#define __FILEHASH_H__
|
||||
|
||||
#include <string>
|
||||
#include "global.h"
|
||||
#include "md5.h"
|
||||
|
||||
#define MD_CTX MD5_CTX
|
||||
#define MDInit MD5Init
|
||||
#define MDUpdate MD5Update
|
||||
#define MDFinal MD5Final
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define HASH_LEVEL_NONE 0
|
||||
#define HASH_LEVEL_PARTITIAL 1
|
||||
#define HASH_LEVEL_FULL 2
|
||||
#define HASH_LEVEL_FRONT_PARTITIAL 3 // 2012-08-13 Content 앞 특정 부분만 Hash 추출을 위해 추가
|
||||
|
||||
/// @brief CFileHash class
|
||||
/// 주 목적은 파일의 해시값을 구하는 것이다.
|
||||
///
|
||||
/// 상황에 따라 세가지 해시방법을 제공한다.
|
||||
/// 1. 파일 전체를 해시.
|
||||
/// 2. 파일의 앞일부와 뒤의 일부를 해시처리 한다.(속도 개선)
|
||||
/// 3. buffer와 buffer의 길이를 입력값으로 하는 buffer 모드.
|
||||
///
|
||||
/// 또한 전달받은 해시와 로컬파일의 해시를 Level에 따라 체크 하는 인터페이스도
|
||||
/// 제공하고있다.
|
||||
///
|
||||
/// 에러 상황은 GetLastErrorMessage() 함수를 통해 어떤이유로 에러가 발생했는지
|
||||
/// 확인 할 수 있다.
|
||||
class CFileHash
|
||||
{
|
||||
public :
|
||||
CFileHash();
|
||||
|
||||
/// @brief Sender에서 사용하는 함수로서 파일에 대한 해쉬값을 레벨에 따라 생성한다.
|
||||
/// @param szFileFullPath [in] 파일의 절대경로 값.
|
||||
/// @param nHashLevel [in] 해싱 방법에 대한 레벨 값.
|
||||
/// @param szResult [out] 해싱된 결과값을 되돌려주는 변수. nHashLevel이 HASH_LEVEL_NONE일 경우 항상 "0000"이 들어감.
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool GetFileHash(const string &szFileFullPath, int nHashLevel, string& szResult);
|
||||
|
||||
/// @brief Receiver에서 사용하는 함수로서 해당파일의 해시값과 Sender에서 받은 해싱값을 비교한 결과를 전달한다.
|
||||
/// @param szFileFullPath [in] 파일의 절대경로 값.
|
||||
/// @param nHashLevel [in] 해싱 방법에 대한 레벨 값.
|
||||
/// @param szCompareHash [out] Sender로 부터 받은 해시 값.
|
||||
/// @return 같은 값일경우 return true, 그 외에는 return false
|
||||
bool CheckFileHash(const string& szFileFullPath, int nHashLevel, const string szCompareHash);
|
||||
|
||||
/// @brief Sender쪽에서 보낼때 파일을 buffer단위로 해싱처리를 하기위한 인터페이스다.
|
||||
/// @param szBuffer [in] 해싱할 데이터
|
||||
/// @param nLen [in] 해싱할 데이터의 길이.
|
||||
/// @return 없음.
|
||||
void UpdateBufferHash(unsigned char* szBuffer, int nLen );
|
||||
|
||||
/// @brief UpdateBufferHash() 함수를 이용해서 해싱한 결과값을 얻어온다.
|
||||
/// @param szResult [out] 결과값을 되돌려주는 변수
|
||||
/// @return 없음.
|
||||
void GetBufferHash(string& szResult);
|
||||
|
||||
/// @brief Buffer Hash 했던 값을 초기화 하는 함수.
|
||||
/// @return 없음.
|
||||
void ClearBufferHash();
|
||||
|
||||
/// @brief HASH_LEVEL_PARTITIAL일 경우 파일의 앞뒤의 일정용량을 해시대상으로 삼는데 그 용량값의 설정값을 얻는다.
|
||||
/// @return 없음.
|
||||
int GetPartitialSize();
|
||||
|
||||
/// @brief HASH_LEVEL_PARTITIAL일 경우 파일의 앞뒤의 일정용량을 해시대상으로 삼는데 그 용량값의 설정값을 세팅한다.
|
||||
/// @param nPartitialSize [in] MByte 단위로 partitial 크기를 설정한다. (0 또는 (-) 음수는 처리안함)
|
||||
/// @return 성공은 return true, 실패는 return false
|
||||
bool SetPartitialSize( int nPartitialSize );
|
||||
|
||||
/// @brief 각 함수 호출시 마지막에 난 에러메세지를 얻을 수 있다.
|
||||
/// @return 마지막에 발생한 에러메세지.
|
||||
string GetLastErrorMessage();
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460, #27533)
|
||||
/// @brief 토토디스크 고객사 지원용 파일 Hash 정보 추출 함수
|
||||
/// @param szFileFullPath [in] 파일의 절대경로 값.
|
||||
/// @param nFileTotalSize [in] 파일 크기 값.
|
||||
/// @param szResultTotosoraMd5 [out] TOTOROSA MD5 추출 결과값을 반환하기 위한 변수.
|
||||
/// @param szResultDnaCheckKey [out] 토토 DNA Check Key 추출 결과값을 반환하기 위한 변수.
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool GetHashTOTO( const string &szFileFullPath, const unsigned long long nFileTotalSize, string& szResultTotosoraMd5, string& szResultDnaCheckKey );
|
||||
|
||||
|
||||
private :
|
||||
int m_nPartitialSize;
|
||||
MD_CTX m_contextForBuffer;
|
||||
char m_szLastErrorMessage[1024];
|
||||
|
||||
bool GetHashFull(const string &szFileFullPath, string& szResult);
|
||||
bool GetHashPartitial(const string &szFileFullPath, string& szResult);
|
||||
bool GetHashFrontPartitial(const string &szFileFullPath, string& szResult);
|
||||
};
|
||||
#endif //__FILEHASH_H__
|
||||
@@ -0,0 +1,706 @@
|
||||
#include "FileSender.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <algorithm> // find 함수 사용 목적
|
||||
|
||||
|
||||
CFileSender::CFileSender(CIPCache * pIpCache, CProcessStatus * pProcess, CSocketControl * pSockCtl )
|
||||
: m_bProcessing( false )
|
||||
, m_bThreadStop( false )
|
||||
, m_pIpCache ( pIpCache )
|
||||
, m_pProcessStatus( pProcess )
|
||||
, m_pSocketControl( pSockCtl )
|
||||
, m_fhsAvailableCount(0)
|
||||
{
|
||||
// NEW 2013-03-19 huibong
|
||||
// Thread 핸들 멤버 변수 초기화 처리 (Valgrind 체크시 수정 필요사항)
|
||||
|
||||
// CHG 2014-05-26 huibong
|
||||
// 본 모듈 특징상 Thread 가 생성되었다가
|
||||
// 1. 지정된 전송 작업을 마친 후 정상적으로 Thread 가 종료됨.
|
||||
// 2. 업무 Flow 에 따라 Thread 가 전송 작업 중 모듈 객체 자체가 소멸될 수 있음.
|
||||
// 이에 따라 Thread Handle 을 정확하게 제어해야 함.
|
||||
// - 잘못 제어할 경우... 다른 Thread Handle 에 명령을 내릴 수 있으므로 주의 (#19102)
|
||||
// 하지만... OS 에 따라 pthread_t Type 이 서로 다름
|
||||
// - FreeBSD : pthread 구조체에 대한 포인터
|
||||
// - Linux : unsigned long integer
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
m_threadHandle = NULL;
|
||||
|
||||
#else // Linux 인 경우...
|
||||
m_threadHandle = 0;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
CFileSender::~CFileSender()
|
||||
{
|
||||
// Thread 동작 정지 처리.
|
||||
#ifdef __FreeBSD__
|
||||
if( m_threadHandle != NULL )
|
||||
pthread_cancel( m_threadHandle );
|
||||
|
||||
#else // Linux 인 경우...
|
||||
if( m_threadHandle != 0 )
|
||||
pthread_cancel( m_threadHandle );
|
||||
|
||||
#endif
|
||||
|
||||
m_listTarget.clear();
|
||||
|
||||
}
|
||||
|
||||
bool CFileSender::Init( const std::string& szFileName, unsigned long long nFileSize, const std::string& szTargetTranId
|
||||
, std::vector<string>& vecTarget, int nHashLevel, bool bUseInternalIp, char command, std::string& szErrorMessage )
|
||||
{
|
||||
// 사용여부 플래그를 확인
|
||||
if( IsIdle() == false )
|
||||
return false;
|
||||
|
||||
// 전달받은 소스 파일 정보 검증.
|
||||
if( CheckFileInfo( szFileName, nFileSize, szErrorMessage ) == false )
|
||||
return false;
|
||||
|
||||
// Target FHS 갯수 검사
|
||||
if( vecTarget.size() <= 0 )
|
||||
{
|
||||
szErrorMessage = "Target FHS count not valid.(";
|
||||
szErrorMessage += vecTarget.size();
|
||||
szErrorMessage += ")";
|
||||
|
||||
LOG( LERR, "%s", szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// NEW 2012-02-01 huibong
|
||||
// 전달받은 Target FHS 정보 중에 현재 FHS ( 원본 ) 가 포함되었는지 검사하는 방어 코드를 추가한다.
|
||||
// 실제 Control 프로세스 개발자가 실수로 원본 FHS 을 Target FHS 로 설정하여 전송 작업으로 인한 원본 파일 유실이 발생함.
|
||||
char tempBuffer[256];
|
||||
memset( tempBuffer, 0x00, sizeof(tempBuffer));
|
||||
if( gethostname( tempBuffer, 255 ) != 0 )
|
||||
{
|
||||
// Current 장비의 hostname 을 가져오지 못할 경우 오류로 처리 및 전송 실패 처리.
|
||||
szErrorMessage = "Source FHS hostname not valid.";
|
||||
LOG( LERR, "%s [%d][%s]", szErrorMessage.c_str(), errno, strerror(errno));
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Current host name 이 정상인 경우 Target FHS 상에 해당 정보가 존재하는지 검사한다.
|
||||
std::string szCurrentHostName = tempBuffer;
|
||||
std::vector<string>::const_iterator it = find( vecTarget.begin(), vecTarget.end(), szCurrentHostName );
|
||||
|
||||
if( it != vecTarget.end() ) // 실제로 현재 FHS 와 Target FHS 정보가 동일할 경우 오류 처리
|
||||
{
|
||||
szErrorMessage = "Source FHS(";
|
||||
szErrorMessage += szCurrentHostName ;
|
||||
szErrorMessage += ") is included Target FHS(";
|
||||
szErrorMessage += *it;
|
||||
szErrorMessage += "). request not valid.";
|
||||
|
||||
LOG( LERR, "%s", szErrorMessage.c_str() );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 정상인 경우 전달받은 정보를 멤버변수에 저장처리후 true 반환.
|
||||
m_szSourceFileName = szFileName;
|
||||
m_nSourceFileSize = nFileSize;
|
||||
m_szTargetTranId = szTargetTranId;
|
||||
m_vecTargetFhs = vecTarget;
|
||||
m_nHashLevel = nHashLevel;
|
||||
m_requestType = command;
|
||||
m_bUseInternalIp = bUseInternalIp;
|
||||
|
||||
// 기타 변수 초기화.
|
||||
m_listTarget.clear();
|
||||
m_fhsAvailableCount = 0;
|
||||
m_szSourceHashValue.clear();
|
||||
m_bThreadStop = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief 파일 전송 관련 작업을 정지시킨다.
|
||||
void CFileSender::Stop()
|
||||
{
|
||||
if( IsIdle() == true )
|
||||
return;
|
||||
|
||||
m_bThreadStop = true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool CFileSender::Start()
|
||||
{
|
||||
int nRet = ::pthread_create( &m_threadHandle, 0, CFileSender::SenderThread, this );
|
||||
if( nRet != 0 )
|
||||
{
|
||||
// Thread 생성 실패시
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "thread create failed.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
|
||||
// 파일 Replication 수행을 위한 Thread 생성 실패시
|
||||
// Control daemon 으로 오류 패킷 전송 처리.
|
||||
m_pSocketControl->SendErrorMessageToControl( "file trnasfer thread create failed.", m_requestType );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief FileSender Class 의 Thread Function, 파일의 전송을 담당함.
|
||||
void * CFileSender::SenderThread( void * arg )
|
||||
{
|
||||
CFileSender * pObject = reinterpret_cast<CFileSender *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
// 파일 Sending 작업을 위한 함수 호출.
|
||||
pObject->SetThreadStart();
|
||||
pObject->Execute();
|
||||
pObject->SetThreadEnd();
|
||||
|
||||
// CHG 2014-05-26 huibong
|
||||
// Thread Handle 관련 변수 초기화 기능 추가
|
||||
#ifdef __FreeBSD__
|
||||
pObject->m_threadHandle = NULL;
|
||||
|
||||
#else // Linux 인 경우...
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
#endif
|
||||
|
||||
// CHG 2013-03-19 huibong
|
||||
// detach 된 Thread 에 대해 pthread_exit() 호출시
|
||||
// 기준 할당 자원에 대한 해지 처리가 되지 않아 메모리 누수 상황이 발생.
|
||||
// 따라서 이를 해결하기 detach 된 thread 에 대해 pthread_exit() 호출 코드 제거.
|
||||
//pthread_exit( NULL );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// @brief 실제 파일의 전송을 처리하기 위한 함수.
|
||||
void CFileSender::Execute()
|
||||
{
|
||||
m_pProcessStatus->AddSendCount();
|
||||
|
||||
// 1. Target FHS 정보 초기화.
|
||||
InitFhsInfo();
|
||||
|
||||
// 2. Target FHS 로 접속 처리.
|
||||
if( ConnectTargetFhs() == false || m_bThreadStop == true )
|
||||
{
|
||||
// 접속을 시도했는데 유효한 Connection 이 1개도 없는 경우
|
||||
m_pProcessStatus->SubtractSendCount();
|
||||
m_listTarget.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 파일의 전송 처리.
|
||||
if( SendFileToFhs() == false || m_bThreadStop == true )
|
||||
{
|
||||
m_pProcessStatus->SubtractSendCount();
|
||||
m_listTarget.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// BUG 2010-11-30 huibong
|
||||
// if 구문 안의 OR 조건이 '||' 아닌 '|' 로 처리되어 이에 대한 수정처리
|
||||
|
||||
// 4. Hash 파일 검사 여부에 따른 처리.
|
||||
if( CheckHashValue() == false || m_bThreadStop == true )
|
||||
{
|
||||
m_pProcessStatus->SubtractSendCount();
|
||||
m_listTarget.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 해당 처리 내역을 Control 로 전송처리
|
||||
if( m_bThreadStop == false )
|
||||
SendTransferResultToControl();
|
||||
|
||||
// 6. 종료 처리
|
||||
m_listTarget.clear();
|
||||
|
||||
m_pProcessStatus->SubtractSendCount();
|
||||
}
|
||||
|
||||
/// @brief 대상 FHS 정보를 설정하고 접속을 설정함.
|
||||
bool CFileSender::InitFhsInfo()
|
||||
{
|
||||
// FHS 정보 저장소 Clear 처리.
|
||||
m_listTarget.clear();
|
||||
|
||||
// 전달받은 FHS 정보를 Target FHS 에 저장처리.
|
||||
std::vector<string>::const_iterator it;
|
||||
for( it = m_vecTargetFhs.begin(); it != m_vecTargetFhs.end(); it++)
|
||||
{
|
||||
CTargetFhsInfo target( *it );
|
||||
m_listTarget.push_back( target );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief 대상 FHS 로 접속 처리를 수행.
|
||||
bool CFileSender::ConnectTargetFhs()
|
||||
{
|
||||
// Target FHS 정보를 이용하여 접속 처리.
|
||||
std::list< CTargetFhsInfo >::iterator it;
|
||||
for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
|
||||
{
|
||||
it->MakeConnection( m_bUseInternalIp, m_pIpCache );
|
||||
it->PrintToConnectionInfo();
|
||||
}
|
||||
|
||||
// 각 Target FHS 로 접속된 유효한 Connection 이 1개로도 있는지 검사한다.
|
||||
// 만약 유효한 Connection 이 1 개라도 존재하지 않는 경우 Control Daemon 에게 오류메시지를 전달하고
|
||||
// 작업을 종료처리한다.
|
||||
|
||||
m_fhsAvailableCount = 0;
|
||||
for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
|
||||
{
|
||||
if( it->IsAvailable() == true )
|
||||
m_fhsAvailableCount++;
|
||||
}
|
||||
|
||||
if( m_fhsAvailableCount == 0 )
|
||||
{
|
||||
// 유효한 Connection 을 가진 FHS 가 존재하지 않는 경우
|
||||
// Control 로 오류 메시지 전송처리.
|
||||
// 재시도에 대한 처리를 위해
|
||||
// 오류로 처리하지 않고 성공으로 처리하여 Control 에서 재시도 처리가 이루어지도록 한다.
|
||||
SendTransferResultToControl();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief 각 FHS 로 파일 전송을 시작한다.
|
||||
bool CFileSender::SendFileToFhs()
|
||||
{
|
||||
// 1. 원본 파일에 대한 Open 처리.
|
||||
std::string szErrorMessage;
|
||||
int sourceFd = open( m_szSourceFileName.c_str(), O_RDONLY );
|
||||
if( sourceFd == -1 )
|
||||
{
|
||||
// 해당 파일을 Open 할 수 없는 경우.
|
||||
// control 로 오류 메시지 전송
|
||||
szErrorMessage = "Source file open fail.(" + m_szSourceFileName + ")";
|
||||
m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 각 FHS 로 파일 전송 요청 메시지 전송.
|
||||
// ** 주의 : Control-Transfer 간 Protocol Command 가
|
||||
// Transfer-Transfer 간 Command 와 다르므로 변환처리하여 전송해야 한다.
|
||||
char transferCommand;
|
||||
switch( m_requestType )
|
||||
{
|
||||
case CONTROL_FILE_REPLICATION:
|
||||
transferCommand = TRANSFER_FILE_REPLICATION;
|
||||
break;
|
||||
|
||||
case CONTROL_FILE_CACHE:
|
||||
transferCommand = TRANSFER_FILE_CACHE;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG( LERR, "Command convert error [%02x]", m_requestType );
|
||||
|
||||
transferCommand = m_requestType;
|
||||
break;
|
||||
}
|
||||
|
||||
std::list< CTargetFhsInfo >::iterator it;
|
||||
m_fhsAvailableCount = 0;
|
||||
for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
|
||||
{
|
||||
if( it->ReadyToFileTransfer( m_szSourceFileName, m_nSourceFileSize, m_szTargetTranId, transferCommand ) == false )
|
||||
{
|
||||
LOG( LERR, "FHS Error [%s][%s]", (it->GetHostName()).c_str(), (it->GetErrorMessage()).c_str() );
|
||||
}
|
||||
|
||||
// 유효 목록 FHS count 갱신
|
||||
if( it->IsAvailable() == true )
|
||||
m_fhsAvailableCount++;
|
||||
}
|
||||
|
||||
// 3. 전송 가능한 FHS 정보 확인.
|
||||
if( m_fhsAvailableCount == 0 )
|
||||
{
|
||||
// 유효한 FHS 가 없는 경우
|
||||
// 오류메시지로 전송해야 하지만 그럴 경우 Control 에서 Retry 를 수행하지 않으므로
|
||||
// 정상적인 동작을 수행시켜 차후에 Retry 가 수행될 수 있도록 수정한다.
|
||||
|
||||
close( sourceFd );
|
||||
|
||||
LOG( LERR, "Available target fhs not exist [%s][%llu]", m_szSourceFileName.c_str(), m_nSourceFileSize );
|
||||
|
||||
SendTransferResultToControl();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 각 FHS 로 파일 전송 시작.
|
||||
// - 우선은 Buffer 를 이용한 전송처리.
|
||||
// TODO 차후에는 Hash Level, 유효 FHS 수를 확인하여 noCopySend (sendfile) 방법으로 전송처리.
|
||||
//if( m_nHashLevel != HASH_LEVEL_FULL || m_fhsAvailableCount == 1 )
|
||||
|
||||
// 0 Byte 파일이 아닌 경우에만 전송 처리.
|
||||
if( m_nSourceFileSize > 0 )
|
||||
{
|
||||
// 2017-12-26 BUG huibong (#31633)
|
||||
// read() 호출 관련 반환값 저장을 위한 nRead 가 size_t(unsigned) 형일 경우
|
||||
// read() 호출시 오류로 인한 상황 인지를 못하게 됨.
|
||||
// 특히 disk 장애시 -1 을 반환하고 errno 에 5(EIO) 값이 저장됨.
|
||||
//size_t nRead = 0;
|
||||
ssize_t nRead = 0;
|
||||
|
||||
size_t nWrite = 0;
|
||||
|
||||
unsigned char fileBuffer[FILE_DATA_BUFFER_SIZE];
|
||||
memset( fileBuffer, 0x00, sizeof(fileBuffer));
|
||||
unsigned long long nTotalSendSize = 0;
|
||||
|
||||
CFileHash * pFileHash = NULL;
|
||||
if( m_nHashLevel == HASH_LEVEL_FULL)
|
||||
{
|
||||
pFileHash = new CFileHash;
|
||||
pFileHash->ClearBufferHash();
|
||||
}
|
||||
|
||||
// EOF를 만날 때 까지 buffer 크기 만큼 읽어 전송 처리 => 이전에 파일 사이즈 검사는 했음.
|
||||
while( (nRead = read( sourceFd, fileBuffer, FILE_DATA_BUFFER_SIZE )) > 0 )
|
||||
{
|
||||
nWrite = (size_t)nRead;
|
||||
|
||||
for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
|
||||
{
|
||||
it->SendBufferToFile( (void *)fileBuffer, nWrite );
|
||||
}
|
||||
|
||||
// File 에 대한 Full Hash 검사시 버퍼의 내용을 Hash 객체에 저장 처리.
|
||||
if( pFileHash != NULL )
|
||||
{
|
||||
pFileHash->UpdateBufferHash( fileBuffer, nRead);
|
||||
}
|
||||
|
||||
nTotalSendSize += nWrite;
|
||||
|
||||
// Thread 정지 처리인 경우
|
||||
if( m_bThreadStop == true )
|
||||
break;
|
||||
}
|
||||
|
||||
// 2017-12-26 BUG huibong (#31633)
|
||||
// disk 장애 발생시 read() 함수는 -1을 반환하고... errno 에 5(EIO) 값이 저장됨.
|
||||
// 이와 관련된 기능 추가
|
||||
if( nRead < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
close( sourceFd );
|
||||
|
||||
// Hash 객체 삭제 처리
|
||||
if( pFileHash != NULL )
|
||||
{
|
||||
delete pFileHash;
|
||||
pFileHash = NULL;
|
||||
}
|
||||
|
||||
// std::string 에서 unsinged long long 값을 문자열로 처리시 문제가 발생하여
|
||||
// C style로 처리함.
|
||||
char tempBuffer[1024];
|
||||
snprintf( tempBuffer, sizeof( tempBuffer ) - 1, "Transfer error. source file[%s][%llu] read[%llu] error(%d:%s)."
|
||||
, m_szSourceFileName.c_str(), m_nSourceFileSize, nTotalSendSize
|
||||
, errorNum, strerror( errorNum ) );
|
||||
|
||||
szErrorMessage = tempBuffer;
|
||||
|
||||
// NEW 2012-02-02 huibong
|
||||
// Content read error 상황을 Control 프로세스에 오류 상황으로 Control 프로세스에 보고 처리한다.
|
||||
m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );
|
||||
|
||||
LOG( LERR, "%s", szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hash 생성시 해당 처리 결과 값을 멤버변수에 저장처리.
|
||||
if( pFileHash != NULL )
|
||||
{
|
||||
pFileHash->GetBufferHash( m_szSourceHashValue );
|
||||
delete pFileHash;
|
||||
pFileHash = NULL;
|
||||
}
|
||||
|
||||
// Thread 정지 처리인 경우
|
||||
if( m_bThreadStop == true )
|
||||
{
|
||||
close( sourceFd );
|
||||
LOG( LWAR, "File[%s] Transfer stop By thread stop flag", m_szSourceFileName.c_str() );
|
||||
|
||||
// CHG 2012-02-02 huibong
|
||||
// File Send Thread 가 중지되는 경우는 현재 Control 과 통신 연결이 끊어진 경우 밖에 없으므로
|
||||
// Control 프로세스로 결과를 전송하는 기능은 탑재하지 않는다..
|
||||
// 만약 Control 가 통신 연결 해제 상황 외에 Thread 가 정지하는 기능을 추가할 경우
|
||||
// Control 로 수행 결과를 전송하는 함수를 호출처리해야 한다.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 만약 전달받은 파일 크기와 실제 전송한 파일의 크기가 다른 경우
|
||||
// 오류 처리 하고 Socket 종료한다. => 이러면 전송된 파일은 자동으로 unlink 처리됨.
|
||||
if( m_nSourceFileSize != nTotalSendSize )
|
||||
{
|
||||
close( sourceFd );
|
||||
|
||||
// std::string 에서 unsinged long long 값을 문자열로 처리시 문제가 발생하여
|
||||
// C style로 처리함.
|
||||
char tempBuffer[1024];
|
||||
snprintf( tempBuffer, sizeof(tempBuffer)-1, "Transfer completed. But source file[%s] size changed. (file: %llu, transfer: %llu)"
|
||||
, m_szSourceFileName.c_str(), m_nSourceFileSize, nTotalSendSize );
|
||||
|
||||
szErrorMessage = tempBuffer;
|
||||
|
||||
// NEW 2012-02-02 huibong
|
||||
// Content 전송 완료 후 원본 파일이 변경된 상황이므로.. Control 프로세스에서 재시도를 못하도록
|
||||
// 오류 상황으로 Control 프로세스에 보고 처리한다.
|
||||
m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );
|
||||
|
||||
LOG( LERR, "%s", szErrorMessage.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_LOG( LINF, "File[%s] Transfer Complete[%llu / %llu]", m_szSourceFileName.c_str(), nTotalSendSize, m_nSourceFileSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LINF, "File[%s] Transfer Complete. 0 byte file", m_szSourceFileName.c_str());
|
||||
}
|
||||
|
||||
// 5. 파일 전송 완료 후 File Descriptor Close
|
||||
close( sourceFd );
|
||||
|
||||
// 6. 전송 결과 응답 대기 처리.
|
||||
for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
|
||||
{
|
||||
if( it->GetFileTransferResult() == false )
|
||||
{
|
||||
LOG( LERR, "Get transfer result failed [%s][%s]", (it->GetHostName()).c_str(), (it->GetErrorMessage()).c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
// 7. 전송 완료 처리가 완료된 유효한 FHS 가 존재하는지 확인.
|
||||
m_fhsAvailableCount = 0;
|
||||
for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
|
||||
{
|
||||
if( it->IsAvailable() == true )
|
||||
m_fhsAvailableCount++;
|
||||
}
|
||||
|
||||
// 8. 전송 가능한 FHS 정보 확인.
|
||||
if( m_fhsAvailableCount == 0 )
|
||||
{
|
||||
// 유효한 FHS 가 없는 경우
|
||||
// 오류메시지로 전송해야 하지만 그럴 경우 Control 에서 Retry 를 수행하지 않으므로
|
||||
// 정상적인 동작을 수행시켜 차후에 Retry 가 수행될 수 있도록 수정한다.
|
||||
LOG( LERR, "Available target fhs not exist by send complete [%s][%llu]", m_szSourceFileName.c_str(), m_nSourceFileSize );
|
||||
|
||||
SendTransferResultToControl();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 9. 전송 완료 처리 후 원본 소스 파일이 변경되었는지 다시 검증
|
||||
if( CheckFileInfo( m_szSourceFileName, m_nSourceFileSize, szErrorMessage ) == false )
|
||||
{
|
||||
// 전송 완료 후 소스 파일 검증 처리시 오류가 발생한 경우.
|
||||
// 이때는 원본 파일이 변경된 경우이므로 Control 에서 재시도를 하지 못하도록 오류로 처리한다.
|
||||
LOG( LERR, "File[%s] transfer completed. But source file size changed.[%s]", m_szSourceFileName.c_str(), szErrorMessage.c_str());
|
||||
|
||||
m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief 파일에 대한 Hash 값을 생성하여 각 FHS 로 전송된 File 의 Hash 검사를 요청한다.
|
||||
bool CFileSender::CheckHashValue()
|
||||
{
|
||||
// Hash 검사를 수행하지 않는 경우 그냥 통과. ^^
|
||||
if( m_nHashLevel != HASH_LEVEL_PARTITIAL && m_nHashLevel != HASH_LEVEL_FULL )
|
||||
return true;
|
||||
|
||||
// 0 Byte 파일인 경우 Hash 검사 그냥 통과.
|
||||
if( m_nSourceFileSize == 0 )
|
||||
return true;
|
||||
|
||||
std::string szErrorMessage;
|
||||
// 자 Hash 검사를 수행하기 위해 Source 파일에 대한 Hash 를 생성한다.
|
||||
if( m_nHashLevel == HASH_LEVEL_PARTITIAL )
|
||||
{
|
||||
// 부분 Hash 검사인 경우.
|
||||
// Hash 처리를 위한 객체를 생성한 후 Hash 검사를 수행
|
||||
CFileHash fileHash;
|
||||
if( fileHash.GetFileHash( m_szSourceFileName, m_nHashLevel, m_szSourceHashValue) == false )
|
||||
{
|
||||
szErrorMessage = "source file hash create fail [" + m_szSourceFileName + "] [" + fileHash.GetLastErrorMessage() + "]";
|
||||
m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );
|
||||
|
||||
LOG( LERR, "%s", szErrorMessage.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Full Hash 인 경우 Socket 으로 데이터 전송시 Source Hash 값이 생성됨.
|
||||
|
||||
// Thread 정지 처리인 경우
|
||||
if( m_bThreadStop == true )
|
||||
{
|
||||
LOG( LERR, "File[%s] Hash check stop By thread stop flag", m_szSourceFileName.c_str() );
|
||||
|
||||
// CHG 2012-02-02 huibong
|
||||
// File Send Thread 가 중지되는 경우는 현재 Control 과 통신 연결이 끊어진 경우 밖에 없으므로
|
||||
// Control 프로세스로 결과를 전송하는 기능은 탑재하지 않는다..
|
||||
// 만약 Control 가 통신 연결 해제 상황 외에 Thread 가 정지하는 기능을 추가할 경우
|
||||
// Control 로 수행 결과를 전송하는 함수를 호출처리해야 한다.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 정상적으로 Hash 생성이 완료된 경우 각 FHS 로 Hash 검사 요청 처리.
|
||||
std::list< CTargetFhsInfo >::iterator it;
|
||||
for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
|
||||
{
|
||||
if( it->SendFileHashCheckRequest( m_nHashLevel, m_nSourceFileSize, m_szSourceHashValue) == false )
|
||||
{
|
||||
LOG( LERR, "Get hash check result failed [%d][%s][%s]", m_nHashLevel, (it->GetHostName()).c_str(), (it->GetErrorMessage()).c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
// 각 FHS 로 부터 처리된 Hash 검사 결과 정보 수신 처리.
|
||||
for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
|
||||
{
|
||||
if( it->GetFileHashCheckResult() == false )
|
||||
{
|
||||
LOG( LERR, "Get hash check result failed [%d][%s][%s]", m_nHashLevel, (it->GetHostName()).c_str(), (it->GetErrorMessage()).c_str() );
|
||||
|
||||
}
|
||||
|
||||
// Thread 정지 처리인 경우
|
||||
if( m_bThreadStop == true )
|
||||
{
|
||||
LOG( LERR, "File[%s] Hash result stop By thread stop flag", m_szSourceFileName.c_str() );
|
||||
|
||||
// CHG 2012-02-02 huibong
|
||||
// File Send Thread 가 중지되는 경우는 현재 Control 과 통신 연결이 끊어진 경우 밖에 없으므로
|
||||
// Control 프로세스로 결과를 전송하는 기능은 탑재하지 않는다..
|
||||
// 만약 Control 가 통신 연결 해제 상황 외에 Thread 가 정지하는 기능을 추가할 경우
|
||||
// Control 로 수행 결과를 전송하는 함수를 호출처리해야 한다.
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief 파일 Transfer 처리 결과를 Control Daemon 으로 전송처리.
|
||||
bool CFileSender::SendTransferResultToControl()
|
||||
{
|
||||
std::map< std::string, std::string> mapSuccessList;
|
||||
std::map< std::string, std::string> mapFailList;
|
||||
|
||||
std::string szHostName;
|
||||
std::string szFileFullName;
|
||||
std::string szErrorMessage;
|
||||
|
||||
// 루프를 돌면서 메시지를 생성
|
||||
std::list< CTargetFhsInfo >::iterator it;
|
||||
for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
|
||||
{
|
||||
if( (it->GetResult( szHostName, szFileFullName, szErrorMessage)) == true )
|
||||
{
|
||||
mapSuccessList[ szHostName ] = szFileFullName;
|
||||
LOG( LDBG, "SEND::SUCCESS [%s]->[%s][%s]", m_szSourceFileName.c_str(), szHostName.c_str(), szFileFullName.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
mapFailList[ szHostName ] = szErrorMessage;
|
||||
LOG( LERR, "SEND::FAIL [%s]->[%s][%s]", m_szSourceFileName.c_str(), szHostName.c_str(), szErrorMessage.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return m_pSocketControl->SendFileReplicationResult( m_requestType, mapSuccessList, mapFailList);
|
||||
}
|
||||
|
||||
/// @brief 전달받은 파일 및 크기가 실제 현재 경로에 존재, 크기가 정확한지 검사하기 위한 함수.
|
||||
bool CFileSender::CheckFileInfo( const std::string& szFileName, unsigned long long nFileSize, std::string& szErrorMessage )
|
||||
{
|
||||
// 1. 전달받은 파일이 존재하는지 검사.
|
||||
if( access(szFileName.c_str(), R_OK) == -1 )
|
||||
{
|
||||
szErrorMessage = "source file access fail.[" + szFileName + "]";
|
||||
LOG( LERR, "%s", szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 파일 크기 검사.
|
||||
FILE * pFile = fopen( szFileName.c_str(), "r+" );
|
||||
if( pFile == NULL )
|
||||
{
|
||||
szErrorMessage = "source file open fail.[" + szFileName + "]";
|
||||
LOG( LERR, "%s", szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseeko( pFile, 0, SEEK_END );
|
||||
off_t totalSize = ftello( pFile );
|
||||
rewind( pFile );
|
||||
fclose( pFile ); // 열려진 descriptor close
|
||||
|
||||
// ADD 2010-12-01 huibong off_t type 확인
|
||||
// /usr/include/sys/_types.h 에 다음과 같이 정의됨. (64Bit, 32Bit 장비 )
|
||||
// typedef __int64_t __off_t; /* file offset */
|
||||
// 또한 printf 포맷 형식 중 %jd => intmax_t 형식이 존재 (/usr/include/machine/stdint.h)
|
||||
// intmax_t 은 /usr/include/machine/_types.h 에 다음과 같이 정의됨 (64Bit,32Bit 동일)
|
||||
// typedef __int64_t __intmax_t;
|
||||
// 이에 따라 snprintf 사용시 off_t totalSize 출력형식을 기존 %lld -> %jd 로 변경처리
|
||||
|
||||
if( (unsigned long long)totalSize != nFileSize )
|
||||
{
|
||||
// std::string 에 숫자를 append 처리시 오류가 자주 발생하여 C Style 로 변환하여 처리함.
|
||||
char tempBuffer[1024];
|
||||
snprintf( tempBuffer, sizeof(tempBuffer)-1
|
||||
, "source file size not match.[ request: %llu, local: %jd ]"
|
||||
, nFileSize, totalSize );
|
||||
|
||||
szErrorMessage = tempBuffer;
|
||||
LOG( LERR, "%s", szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 모두 정상인 경우.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/***************************************************************************
|
||||
ftsd ( File TranSfer Daemon ) Header ( FileSender.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/13
|
||||
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 __FILE_SENDER_H__
|
||||
#define __FILE_SENDER_H__
|
||||
|
||||
#include "Logger.h"
|
||||
#include "IPCache.h"
|
||||
#include "ProcessStatus.h"
|
||||
#include "SocketControl.h"
|
||||
#include "TargetFhsInfo.h"
|
||||
#include "FileHash.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
|
||||
class CFileSender
|
||||
{
|
||||
public :
|
||||
|
||||
CFileSender(CIPCache * pIpCache, CProcessStatus * pProcess, CSocketControl * pSockCtl );
|
||||
|
||||
~CFileSender();
|
||||
|
||||
bool Init( const std::string& szFileName, unsigned long long nFileSize, const std::string& szTargetTranId,
|
||||
std::vector<string>& vecTarget, int nHashLevel, bool bUseInternalIp, char command, std::string& szErrorMessage );
|
||||
|
||||
bool Start( void );
|
||||
|
||||
/// @brief 파일 전송 관련 작업을 정지시킨다.
|
||||
void Stop(void);
|
||||
|
||||
// 현재 객체를 사용할 수 있는지 여부.
|
||||
// File 관련 작업중인 경우 false, Idle 상태인 경우 true 를 반환한다.
|
||||
bool IsIdle( void ) { return !m_bProcessing; }
|
||||
|
||||
void SetThreadStart(void) { m_bProcessing = true; }
|
||||
void SetThreadEnd(void) { m_bProcessing = false; }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/// @brief FileSender Class 의 Thread Function, 파일의 전송을 담당함.
|
||||
static void * SenderThread( void * arg );
|
||||
|
||||
/// @brief 실제 파일의 전송을 처리하기 위한 함수.
|
||||
void Execute(void);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
/// @brief 대상 FHS 정보를 설정하고 접속을 설정함.
|
||||
bool InitFhsInfo(void);
|
||||
|
||||
/// @brief 전달받은 파일 및 크기가 실제 현재 경로에 존재, 크기가 정확한지 검사하기 위한 함수.
|
||||
bool CheckFileInfo( const std::string& szFileName, unsigned long long nFileSize, std::string& szErrorMessage );
|
||||
|
||||
/// @brief 대상 FHS 로 접속 처리를 수행.
|
||||
bool ConnectTargetFhs(void);
|
||||
|
||||
/// @brief 각 FHS 로 파일 전송을 시작한다.
|
||||
bool SendFileToFhs(void);
|
||||
|
||||
/// @brief 파일에 대한 Hash 값을 생성하여 각 FHS 로 전송된 File 의 Hash 검사를 요청한다.
|
||||
bool CheckHashValue(void);
|
||||
|
||||
/// @brief 파일 Transfer 처리 결과를 Control Daemon 으로 전송처리.
|
||||
bool SendTransferResultToControl(void);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool m_bProcessing; // 현재 CFileSender 객체가 작업 중인지 여부를 저장하기 위한 변수.
|
||||
|
||||
bool m_bThreadStop; // Thread 의 동작 정지 여부를 판단하기 위한 변수
|
||||
|
||||
|
||||
// 전역 Object 에 대한 멤버 포인터.
|
||||
CIPCache * m_pIpCache;
|
||||
CProcessStatus * m_pProcessStatus;
|
||||
|
||||
// Control Daemon 과 연결된 Socket Control Object Pointer
|
||||
CSocketControl * m_pSocketControl;
|
||||
|
||||
std::string m_szSourceFileName; // Source File Full Name
|
||||
unsigned long long m_nSourceFileSize; // Source File Size
|
||||
std::string m_szTargetTranId; // RC 간 동기화시 사용할 Target Tran ID 정보
|
||||
std::vector< string > m_vecTargetFhs; // 복제 대상 FHS 정보
|
||||
int m_nHashLevel; // Hash 검사 Level
|
||||
char m_requestType; // 명령어 정보, Replication or Cache
|
||||
bool m_bUseInternalIp; // 내부망 사용 여부.
|
||||
|
||||
// 쓰레드 핸들
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Target FHS Handle 정보를 저장하기 위한 멤버 변수
|
||||
std::list< CTargetFhsInfo > m_listTarget;
|
||||
|
||||
int m_fhsAvailableCount;
|
||||
|
||||
std::string m_szSourceHashValue;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif /* __FILE_SENDER_H__ */
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
#include "GetConfig.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/// @brief config value chech
|
||||
/// @param szValue [in] value
|
||||
/// @param conf [in] Config 객체
|
||||
/// @param defsection [in] default section
|
||||
/// @param defkey [in] default key
|
||||
/// @return 성공시 true, 오류 발생시 false 를 반환.
|
||||
static bool CheckValue(std::string& szValue, Config& conf, const std::string& defsection, const std::string& defkey)
|
||||
{
|
||||
if( szValue.size() > 0 )
|
||||
return true;
|
||||
|
||||
if( defsection.size() <= 0 || defkey.size() <= 0 )
|
||||
return false;
|
||||
|
||||
conf.GetConfig( defsection, defkey, szValue );
|
||||
|
||||
if( szValue.size() > 0 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GetConfig( const std::string& szConfFile, struct st_config& stConfig, std::string& errorMessage)
|
||||
{
|
||||
// Config 처리를 위한 객체 생성
|
||||
Config conf;
|
||||
|
||||
// Config File open
|
||||
if( conf.Open( szConfFile) == false )
|
||||
{
|
||||
errorMessage = "Config file open failed.[" + szConfFile + "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 각 Section , Key 값 파싱 및 저장처리
|
||||
std::string szValue;
|
||||
|
||||
// [common]1. log path
|
||||
if( conf.GetConfig( PROG_NAME, "DEFAULT_LOG_DIR", szValue ) == false )
|
||||
{
|
||||
if( !CheckValue(szValue, conf, "COMMON", "DEFAULT_LOG_DIR") )
|
||||
{
|
||||
errorMessage = "Config info get failed. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->LOG_DIR";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
stConfig.szLogDir = szValue;
|
||||
szValue.clear();
|
||||
|
||||
// [common]2. file storage root
|
||||
if( conf.GetConfig( PROG_NAME, "FILE_STORAGE_ROOT", szValue ) == false )
|
||||
{
|
||||
if( !CheckValue(szValue, conf, "COMMON", "FILE_STORAGE_ROOT") )
|
||||
{
|
||||
errorMessage = "Config info get failed. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->FILE_STORAGE_ROOT";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
stConfig.szStorageRoot = szValue;
|
||||
szValue.clear();
|
||||
|
||||
// [common]3. log level
|
||||
if( conf.GetConfig( PROG_NAME, "LOG_LEVEL", szValue ) == false )
|
||||
{
|
||||
if( !CheckValue(szValue, conf, "COMMON", "LOG_LEVEL") )
|
||||
{
|
||||
errorMessage = "Config info get failed. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->LOG_LEVEL";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
stConfig.nLogLevel = atoi(szValue.c_str());
|
||||
szValue.clear();
|
||||
|
||||
// [common]4. internal nic name
|
||||
if( conf.GetConfig( PROG_NAME, "INTERNAL_NIC_NAME", szValue ) == false )
|
||||
{
|
||||
if( !CheckValue(szValue, conf, "COMMON", "INTERNAL_NIC_NAME") )
|
||||
{
|
||||
errorMessage = "Config info get failed. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->INTERNAL_NIC_NAME";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
stConfig.szInternalNicName = szValue;
|
||||
szValue.clear();
|
||||
|
||||
// [ftsd]1. worker process count
|
||||
if( conf.GetConfig( PROG_NAME, "WORKER_PROCESS_COUNT", szValue ) == false )
|
||||
{
|
||||
errorMessage = "Config info get failed. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->WORKER_PROCESS_COUNT";
|
||||
return false;
|
||||
}
|
||||
if( !CheckValue(szValue, conf, "", "") )
|
||||
{
|
||||
errorMessage = "Config info get empty. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->WORKER_PROCESS_COUNT";
|
||||
return false;
|
||||
}
|
||||
stConfig.nWorkerProcessCount = atoi(szValue.c_str());
|
||||
szValue.clear();
|
||||
|
||||
// [ftsd]2. max file send thread count
|
||||
if( conf.GetConfig( PROG_NAME, "MAX_FILE_SEND_THREAD", szValue ) == false )
|
||||
{
|
||||
errorMessage = "Config info get failed. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->MAX_FILE_SEND_THREAD";
|
||||
return false;
|
||||
}
|
||||
if( !CheckValue(szValue, conf, "", "") )
|
||||
{
|
||||
errorMessage = "Config info get empty. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->MAX_FILE_SEND_THREAD";
|
||||
return false;
|
||||
}
|
||||
stConfig.nMaxFileSendThread = atoi(szValue.c_str());
|
||||
szValue.clear();
|
||||
|
||||
// [ftsd]3. fhs internal ip cache timeout
|
||||
if( conf.GetConfig( PROG_NAME, "FHS_INTERNAL_IP_CACHE_TIMEOUT", szValue ) == false )
|
||||
{
|
||||
errorMessage = "Config info get failed. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->FHS_INTERNAL_IP_CACHE_TIMEOUT";
|
||||
return false;
|
||||
}
|
||||
if( !CheckValue(szValue, conf, "", "") )
|
||||
{
|
||||
errorMessage = "Config info get empty. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->FHS_INTERNAL_IP_CACHE_TIMEOUT";
|
||||
return false;
|
||||
}
|
||||
stConfig.nFhsInternalIpCacheTimeout = atoi(szValue.c_str());
|
||||
szValue.clear();
|
||||
|
||||
// [ftsd]4. proper storage refresh preiod(sec)
|
||||
if( conf.GetConfig( PROG_NAME, "PROPER_STORAGE_REFRESH_PERIOD", szValue ) == false )
|
||||
{
|
||||
errorMessage = "Config info get failed. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->PROPER_STORAGE_REFRESH_PERIOD";
|
||||
return false;
|
||||
}
|
||||
if( !CheckValue(szValue, conf, "", "") )
|
||||
{
|
||||
errorMessage = "Config info get empty. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->PROPER_STORAGE_REFRESH_PERIOD";
|
||||
return false;
|
||||
}
|
||||
stConfig.nProperStorageRefresPeriod = atoi(szValue.c_str());
|
||||
szValue.clear();
|
||||
|
||||
// [ftsd]5. max network bandwidth use amount(%)
|
||||
if( conf.GetConfig( PROG_NAME, "MAX_BANDWIDTH_USE", szValue ) == false )
|
||||
{
|
||||
errorMessage = "Config info get failed. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->MAX_BANDWIDTH_USE";
|
||||
return false;
|
||||
}
|
||||
if( !CheckValue(szValue, conf, "", "") )
|
||||
{
|
||||
errorMessage = "Config info get empty. [";
|
||||
errorMessage += PROG_NAME;
|
||||
errorMessage += "]->MAX_BANDWIDTH_USE";
|
||||
return false;
|
||||
}
|
||||
stConfig.nMaxBandwidthUse = atoi(szValue.c_str());
|
||||
szValue.clear();
|
||||
|
||||
// 성공시
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/***************************************************************************
|
||||
ftsd ( File TranSfer Daemon ) Header ( GetConfig.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/03
|
||||
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 __GET_CONFIG_H__
|
||||
#define __GET_CONFIG_H__
|
||||
|
||||
#include "../define_ftsd.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief config file parsing and save to stConfig function
|
||||
/// @param szConfFile [in] Conf 파일에 대한 경로 및 파일 정보
|
||||
/// @param stConfig [out] Conf 파일의 정보를 저장하기 위한 st_config 참조변수
|
||||
/// @param errorMessage [out] 오류발생시 해당 오류 정보를 저장하여 반환하기 위한 참조변수
|
||||
/// @return 성공시 true, 오류 발생시 false 를 반환. 상세한 오류 정보는 errorMessage 변수에 저장됨.
|
||||
bool GetConfig( const std::string& szConfFile, struct st_config& stConfig, std::string& errorMessage);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __GET_CONFIG_H__ */
|
||||
@@ -0,0 +1,132 @@
|
||||
#include <iostream>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "IPCache.h"
|
||||
|
||||
#define MX_LOCK_MAP()\
|
||||
do \
|
||||
{\
|
||||
pthread_mutex_lock(&m_mx_IPPairMap);\
|
||||
} while( 0 );
|
||||
|
||||
#define MX_UNLOCK_MAP()\
|
||||
do \
|
||||
{\
|
||||
pthread_mutex_unlock(&m_mx_IPPairMap);\
|
||||
} while( 0 );
|
||||
|
||||
|
||||
// initialize class
|
||||
CPrivateIP::CPrivateIP()
|
||||
: m_time_lastUpdated ( 0L )
|
||||
{
|
||||
}
|
||||
|
||||
CPrivateIP::CPrivateIP(const std::string szPrivateIP, const long time_lastUpdated )
|
||||
: m_szPrivateIP ( szPrivateIP )
|
||||
, m_time_lastUpdated ( time_lastUpdated )
|
||||
{
|
||||
}
|
||||
|
||||
// initialize class
|
||||
CIPCache::CIPCache()
|
||||
{
|
||||
m_time_timeout = 300; // 5min ( default 300 second )
|
||||
//m_mx_IPPairMap = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_init( &m_mx_IPPairMap, NULL );
|
||||
}
|
||||
|
||||
CIPCache::~CIPCache()
|
||||
{
|
||||
pthread_mutex_destroy(&m_mx_IPPairMap);
|
||||
}
|
||||
|
||||
bool CIPCache::SetTimeout(const long time_timeout)
|
||||
{
|
||||
if ( time_timeout < 0 )
|
||||
{
|
||||
// time is equal or bigger than 0
|
||||
return false;
|
||||
}
|
||||
|
||||
m_time_timeout = time_timeout;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIPCache::GetTimeout(long& time_timeout)
|
||||
{
|
||||
time_timeout = m_time_timeout;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIPCache::GetPrivateIP( const std::string& szPublic, std::string& szPrivate )
|
||||
{
|
||||
time_t time_now;
|
||||
time_t res = 0;
|
||||
|
||||
// Public ip check
|
||||
if (szPublic.empty() == true)
|
||||
return false;
|
||||
|
||||
res = time( &time_now );
|
||||
if ( res < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MX_LOCK_MAP();
|
||||
|
||||
IPPairMap::iterator it;
|
||||
it = m_IPPair.find(szPublic);
|
||||
|
||||
if ( it == m_IPPair.end() ) // Not exist...
|
||||
{
|
||||
MX_UNLOCK_MAP();
|
||||
return false;
|
||||
}
|
||||
else if ( time_now - it->second.m_time_lastUpdated > (long)m_time_timeout ) // Cache Time out
|
||||
{
|
||||
m_IPPair.erase(it);
|
||||
|
||||
MX_UNLOCK_MAP();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
szPrivate = it->second.m_szPrivateIP;
|
||||
|
||||
MX_UNLOCK_MAP();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CIPCache::SetPrivateIP( const std::string& szPublic, std::string& szPrivate )
|
||||
{
|
||||
time_t time_now;
|
||||
time_t res = 0;
|
||||
|
||||
// check valid ip
|
||||
if( (int)inet_addr(szPrivate.c_str()) == -1 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
res = time( &time_now );
|
||||
if ( res < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MX_LOCK_MAP();
|
||||
|
||||
m_IPPair[szPublic].m_szPrivateIP = szPrivate;
|
||||
m_IPPair[szPublic].m_time_lastUpdated = time_now;
|
||||
|
||||
MX_UNLOCK_MAP();
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/***************************************************************************
|
||||
IP Cache Class
|
||||
-----------------------------------------
|
||||
begin : 2010/03/02
|
||||
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 __IPCACHE_H__
|
||||
#define __IPCACHE_H__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
|
||||
/// @brief CIPCache class
|
||||
class CPrivateIP
|
||||
{
|
||||
public :
|
||||
CPrivateIP();
|
||||
CPrivateIP(const std::string szPrivateIP, const long time_lastUpdated );
|
||||
|
||||
/// @brief 사설 IP값을 저장한다.
|
||||
std::string m_szPrivateIP;
|
||||
long m_time_lastUpdated;
|
||||
private :
|
||||
};
|
||||
|
||||
|
||||
/// @brief CIPCache class
|
||||
class CIPCache
|
||||
{
|
||||
public :
|
||||
|
||||
CIPCache();
|
||||
~CIPCache();
|
||||
|
||||
/// @brief 요청된 Host명(혹은 공인IP)에 대해 캐시된 데이터를 반환한다.
|
||||
/// @param szPublic [in] 요청 Host명 값.
|
||||
/// @param szPrivate [out] 요청 Host에 매칭되는 Private IP값.
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool GetPrivateIP( const std::string& szPublic, std::string& szPrivate);
|
||||
|
||||
/// @brief 요청된 Host명(혹은 공인IP)에 대해 캐시된 데이터를 저장한다.
|
||||
/// @param szPublic [in] 요청 Host명 값.
|
||||
/// @param szPrivate [in] 요청 Host에 매칭되는 Private IP값.
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool SetPrivateIP(const std::string& szPublic, std::string& szPrivate);
|
||||
|
||||
/// @brief timeout 만료 기준을 설정한다. default = 300.
|
||||
/// @param time_timeout [in] 만료 설정 할 초(second).
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool SetTimeout(const long time_timeout);
|
||||
|
||||
/// @brief 설정된 timeout 만료 값을 얻어온다. default = 300.
|
||||
/// @param time_timeout [out] 만료 설정 할 초(second).
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool GetTimeout(long& time_timeout);
|
||||
|
||||
|
||||
private :
|
||||
/// @brief IP Pair를 저장하는 map
|
||||
typedef std::map<std::string, CPrivateIP> IPPairMap;
|
||||
|
||||
/// @brief SetTimeout()을 통해 설정되는 값.
|
||||
long m_time_timeout;
|
||||
|
||||
/// @brief Public IP와 Private IP의 쌍을 가지는 Map, 최근 수정 시각을 포함한다.
|
||||
IPPairMap m_IPPair;
|
||||
|
||||
/// @brief ipPairMap의 mutex
|
||||
pthread_mutex_t m_mx_IPPairMap;
|
||||
};
|
||||
|
||||
#endif //__IPCACHE_H__
|
||||
@@ -0,0 +1,585 @@
|
||||
#include "Listener.h"
|
||||
|
||||
#include "GetConfig.h"
|
||||
#include "Logger.h"
|
||||
#include "Worker.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
// Accept 대기 Count 수
|
||||
// CHG 2010-03-18 huibong
|
||||
// Accept 접속 대기 수가 몰리는 현상이 발생하여 5->30 으로 변경함.
|
||||
#define DEFAULT_ACCEPT_WAIT_COUNT 30
|
||||
|
||||
// 전역 객체 정보
|
||||
// 1. config 정보를 저장하기 위한 전역 객체
|
||||
struct st_config g_envConfig;
|
||||
|
||||
// 2. Listener Socket
|
||||
// => 시그널 처리시 문제로 인해 전역함수로 생성. initialize listener.cpp
|
||||
int g_listenSocket = -1;
|
||||
|
||||
|
||||
int main( int argc, char * argv[] )
|
||||
{
|
||||
// Version 정보 표시
|
||||
if( argc == 2 && strcmp( argv[1], "-v") == 0)
|
||||
{
|
||||
cerr << "[info] " << PROG_NAME << " (File TranSfer Daemon) Version : " << PROG_VERSION << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// 프로그램 중복 실행 체크
|
||||
if( IsCurrentProcessRun() == true )
|
||||
{
|
||||
cerr << "[warning] Process[" << PROG_NAME << "] is already running...." << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// Config 정보 설정 및 전역 정보 설정
|
||||
// extern struct st_config g_envConfig => define_ftsd.h 파일 참조
|
||||
string szErrorMessage;
|
||||
if( GetConfig( DEFAULT_CONFIG_FILE, g_envConfig, szErrorMessage ) == false )
|
||||
{
|
||||
cerr << "[error] Process[" << PROG_NAME << "]: " << szErrorMessage << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Log 처리 객체 생성
|
||||
if( CLogger::Init( PROG_NAME, g_envConfig.szLogDir.c_str(), LINF ) == false )
|
||||
{
|
||||
cerr << "[error] Process[" << PROG_NAME << "]: Log init failed." << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// Process 기동 관련 정보 로깅
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
_LOG( LINF, " %s (File TranSfer Daemon) Start. Version: %s", PROG_NAME, PROG_VERSION );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
_LOG( LINF, "Config file : %s", DEFAULT_CONFIG_FILE );
|
||||
_LOG( LINF, "Listen Port : %d", SVC_LISTEN_PORT );
|
||||
_LOG( LINF, "Log Path : %s/%s", g_envConfig.szLogDir.c_str(), PROG_NAME );
|
||||
_LOG( LINF, "Storage root : %s", g_envConfig.szStorageRoot.c_str() );
|
||||
_LOG( LINF, "Internal NIC : %s", g_envConfig.szInternalNicName.c_str() );
|
||||
_LOG( LINF, "Worker Process : %d", g_envConfig.nWorkerProcessCount );
|
||||
_LOG( LINF, "Max Send Thread : %d", g_envConfig.nMaxFileSendThread );
|
||||
_LOG( LINF, "FHS Cache Time : %d (sec)", g_envConfig.nFhsInternalIpCacheTimeout );
|
||||
_LOG( LINF, "Stroage Refresh : %d (sec)", g_envConfig.nProperStorageRefresPeriod );
|
||||
_LOG( LINF, "MAX Bandwidth : %d (%%)", g_envConfig.nMaxBandwidthUse );
|
||||
_LOG( LINF, "Logging Level : %d", g_envConfig.nLogLevel );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
|
||||
// Log Level 재설정
|
||||
#ifdef _DEBUG_
|
||||
if( CLogger::GetInstance() != NULL )
|
||||
{
|
||||
CLogger::GetInstance()->SetLogLevel( LDBG );
|
||||
}
|
||||
#else
|
||||
if( CLogger::GetInstance() != NULL )
|
||||
{
|
||||
CLogger::GetInstance()->SetLogLevel( g_envConfig.nLogLevel);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// 프로그램 Daemonize
|
||||
if( MakeDaemon() != 0 )
|
||||
{
|
||||
LOG( LERR, "Process[%s] Daemonize failed.", PROG_NAME);
|
||||
cerr << "[error] Process[" << PROG_NAME << "] Daemonize failed." << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Listener 프로세스의 Signal 처리기 설정
|
||||
SetSignalListener();
|
||||
|
||||
// Listen Socket 생성
|
||||
if( MakeListenSocket( g_listenSocket ) == false )
|
||||
{
|
||||
LOG( LERR, "Listen Socket Create failed. => Process Exit." );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Worker Process 생성
|
||||
if( MakeProcessWorker( g_listenSocket ) == false )
|
||||
{
|
||||
close( g_listenSocket );
|
||||
LOG( LERR, "Worker Process make failed. => Listener Process exit. Goob Bye..");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Listener Process : 그냥 대기
|
||||
#if defined(__FreeBSD__)
|
||||
setproctitle( "Listener [monitor worker process]" );
|
||||
#endif
|
||||
while( 1 )
|
||||
{
|
||||
// Listener Process 는 할 일이 없다.
|
||||
// => Worker 프로세스 종료시 SIGCHLD 신호로 인해 신호처리기에서 Worker Process 재성성 수행함.
|
||||
// => 따라서 그냥 시그널 대기
|
||||
pause();
|
||||
//LOG( LNOT, "Listener process receive signal. -> suspend" );
|
||||
}
|
||||
|
||||
// 다음의 코드는 Daemon 으로 동작하기 때문에 수행되지 않는다.
|
||||
close( g_listenSocket );
|
||||
CLogger::Exit();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/// @brief Worker Process 생성(fork) 처리 함수
|
||||
/// @param listenSocket [in] Listen Socket
|
||||
/// @return 생성 성공시 true, 실패시에는 false 를 반환.
|
||||
bool MakeProcessWorker( int listenSocket )
|
||||
{
|
||||
pid_t processId;
|
||||
|
||||
// Conf 에 정의된 Worker Process Count 만큼 Worker 프로세스 생성 처리.
|
||||
for( int index = 0; index < g_envConfig.nWorkerProcessCount; index++ )
|
||||
{
|
||||
// Worker 프로세스 fork
|
||||
processId = fork();
|
||||
if( processId < 0 ) // Fork fail
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Worker Process[%d th] create failed. [%d][%s]", index, errorNum, strerror(errorNum) );
|
||||
return false;
|
||||
}
|
||||
else if( processId == 0 ) // Child Process => Worker Process
|
||||
{
|
||||
// Worker Process Main 함수 호출 및 종료처리.
|
||||
WorkerMain( listenSocket );
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parent Process => Logging
|
||||
_LOG( LINF, "Worker Process[%d th] create success. PID[%d]" , index, processId);
|
||||
|
||||
// 잠시 대기
|
||||
// CHG 2015-09-14 huibong
|
||||
// - #24376 usleep -> nanosleep 으로 변경 처리.
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 200000000; // 0.2 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Worker Process 재생성(fork) 처리 함수
|
||||
/// @param listenSocket [in] Listen Socket
|
||||
/// @return 생성 성공시 true, 실패시에는 false 를 반환.
|
||||
bool RemakeProcessWorker( int listenSocket )
|
||||
{
|
||||
pid_t processId;
|
||||
|
||||
// Worker 프로세스 fork
|
||||
processId = fork();
|
||||
if( processId < 0 ) // Fork fail
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Worker Process recreate failed. [%d][%s]", errorNum, strerror(errorNum) );
|
||||
return false;
|
||||
}
|
||||
else if( processId == 0 ) // Child Process => Worker Process
|
||||
{
|
||||
// Worker Process Main 함수 호출 및 종료처리.
|
||||
WorkerMain( listenSocket );
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parent Process => Logging
|
||||
LOG( LWAR, "Worker Process recreate success. PID[%d]" , processId);
|
||||
|
||||
// 잠시 대기
|
||||
// CHG 2015-09-14 huibong
|
||||
// - #24376 usleep -> nanosleep 으로 변경 처리.
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 200000000; // 0.2 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// @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 )
|
||||
{
|
||||
cerr << "[error] Process duplication check failed.[popen error][" << strerror(errno) << "]" << endl;
|
||||
|
||||
// 오류 발생시 true 반환하여 프로세스 실행 방지처리
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( tempBuffer, 0x00, sizeof(tempBuffer) );
|
||||
while( fgets( tempBuffer, sizeof(tempBuffer)-1, fd) != NULL )
|
||||
{
|
||||
string tempPid( tempBuffer );
|
||||
Trim(tempPid);
|
||||
|
||||
if( atoi( tempPid.c_str() ) != getpid() )
|
||||
{
|
||||
cout << "[info] Process duplication found. pid[" << atoi( tempPid.c_str() ) << "]" << endl;
|
||||
|
||||
bRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pclose( fd );
|
||||
return bRun;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
|
||||
/// @return void
|
||||
void Trim( string & str )
|
||||
{
|
||||
if( str.length() == 0 )
|
||||
return ;
|
||||
|
||||
// 문자열 뒤의 공백, TAB, CR 등의 문자 제거처리.
|
||||
string::size_type pos = str.find_last_not_of(" \a\b\f\n\r\t\v");
|
||||
if( pos != string::npos )
|
||||
str.erase( pos + 1 );
|
||||
|
||||
// 문자열 앞의 공백, TAB, CR 등의 문자 제거처리.
|
||||
pos = str.find_first_not_of(" \a\b\f\n\r\t\v");
|
||||
if( pos != string::npos )
|
||||
str.erase( 0, pos );
|
||||
}
|
||||
|
||||
/// @brief Daemon Process 생성처리
|
||||
/// @return 0 인 경우 성공, -1 인 경우 오류발생
|
||||
int MakeDaemon()
|
||||
{
|
||||
pid_t processId;
|
||||
|
||||
processId = fork();
|
||||
if( processId == -1 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
cout << "[error] " << PROG_NAME << ": daemon fork fail.[" << strerror(errorNum) << "]" << endl;
|
||||
LOG( LERR, "Daemon fork failed. [%d][%s]", errorNum, strerror(errorNum));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( processId != 0 )
|
||||
{
|
||||
// Parent Process => exit
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
else
|
||||
{
|
||||
// NEW 2010-11-30 huibong
|
||||
// Daemon 생성시 파일 권한처리 관련 umask 코드 추가.
|
||||
umask(0);
|
||||
setsid();
|
||||
}
|
||||
|
||||
// NEW 2010-11-30 huibong
|
||||
// Daemon 생성시 stdin, stdout, stderr 처리 Miss로 인해
|
||||
// 원격 ssh 를 이용한 기동 처리시 CTRL+C 를 눌러야 기동처리되는 버그 수정
|
||||
//fclose(stdin);
|
||||
//fclose(stdout);
|
||||
//fclose(stderr);
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// @brief Listener Process 종료 Signal 을 전달받은 경우 이를 처리하기 위한 함수.
|
||||
/// @param nSignalNumber [in] 발생한 시그널 Number
|
||||
/// @return void
|
||||
static void SigTermListener( int nSignalNumber )
|
||||
{
|
||||
// Signal Number 에 따른 로깅처리.
|
||||
if( nSignalNumber == SIGTERM )
|
||||
{
|
||||
LOG( LWAR, "Listener Process[%d] exit job start by user signal [SIGTERM]", getpid() );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LWAR, "Listener Process[%d] exit job start by abnormal signal [%d]", getpid(), nSignalNumber );
|
||||
}
|
||||
|
||||
// Process 종료관련 작업 추가
|
||||
close( g_listenSocket );
|
||||
|
||||
LOG( LWAR, "Listener Process[%d] exit job end. Good Bye..", getpid());
|
||||
|
||||
|
||||
CLogger::Exit();
|
||||
|
||||
|
||||
// CHG 2015-09-14 huibong
|
||||
// - #24376 usleep -> nanosleep 으로 변경 처리.
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
/// @brief Listener Process 에서 Core 생성 관련 Signal 을 받은 경우 이를 처리하기 위한 함수.
|
||||
/// @param nSignalNumber [in] 발생한 시그널 Number
|
||||
/// @return void
|
||||
static void SigCoreListener( int nSignalNumber )
|
||||
{
|
||||
LOG( LERR, "Listener Process[%d] abnoraml exit. Check Core file. Receive signal [%d]", getpid(), nSignalNumber );
|
||||
LOG( LERR, "Core file path : [%s/%s.core]", g_envConfig.szLogDir.c_str(), PROG_NAME );
|
||||
|
||||
// Process 종료관련 작업 추가
|
||||
close( g_listenSocket );
|
||||
CLogger::Exit();
|
||||
|
||||
// Log Directory 상에 Core 파일 생성처리.
|
||||
chdir( g_envConfig.szLogDir.c_str() );
|
||||
|
||||
signal( nSignalNumber, SIG_DFL );
|
||||
|
||||
// Core dump 생성을 위한 신호 발생처리.=> ?
|
||||
raise( nSignalNumber );
|
||||
}
|
||||
|
||||
/// @brief Listener Process 에서 자식 프로세스인 Worker Process 가 종료된 경우 시그널 처리를 위한 함수
|
||||
/// @param nSignalNumber [in] 발생한 시그널 Number
|
||||
/// @return void
|
||||
static void SigChldListener( int nSignalNumber )
|
||||
{
|
||||
pid_t killPid;
|
||||
int nKillStatus;
|
||||
|
||||
while( ( killPid = waitpid( -1, &nKillStatus, WNOHANG ) ) > 0 )
|
||||
{
|
||||
// 자식 프로세스가 Signal 에 의해 종료되었는지 검사.
|
||||
if( WIFSIGNALED( nKillStatus ) )
|
||||
{
|
||||
LOG( LWAR, "Worker process[%d] killed by signal[%d]", killPid, WTERMSIG( nKillStatus ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LWAR, "Worker process[%d] killed. Not signal", killPid );
|
||||
|
||||
// CHG 2014-02-28 huibong
|
||||
// Worker 프로세스가 EXIT_FAILURE 반환 ( 초기화 실패시 )
|
||||
// 해당 내역을 화면 및 로그 상에 출력하고
|
||||
// 자식 프로세스를 재생성 처리하지 않는다.
|
||||
|
||||
if( WIFEXITED( nKillStatus ) )
|
||||
{
|
||||
if( WEXITSTATUS( nKillStatus ) == EXIT_FAILURE )
|
||||
{
|
||||
_LOG( LERR, "Worker Process[%d] initilaize failed.", killPid );
|
||||
_LOG( LERR, "Program abnormally exited because Worker initialize faild..");
|
||||
|
||||
// Main 종료 시그널 발생
|
||||
raise( SIGTERM );
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Worker Process 재생성 처리.
|
||||
if( RemakeProcessWorker( g_listenSocket ) == false )
|
||||
{
|
||||
// Worker Process 재성성 실패시 => 그냥 로깅
|
||||
LOG( LERR, "Worker process recreate failed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LWAR, "Worker process recreate success by SIGCHLD");
|
||||
}
|
||||
}
|
||||
|
||||
// 오류 발생시 해당 내역 로깅
|
||||
if( killPid < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Listener process error: SIG_CHLD receive but waitpid return error[%d][%s]", errorNum, strerror(errorNum));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// @brief Listener프로세스 signal 처리 설정을 위한 함수
|
||||
/// @return void
|
||||
void SetSignalListener()
|
||||
{
|
||||
sigset_t set;
|
||||
struct sigaction act;
|
||||
|
||||
sigfillset( &set );
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
|
||||
memset( &act, 0x00, sizeof(act) );
|
||||
sigfillset( &act.sa_mask );
|
||||
|
||||
/* 무시할 신호 목록 */
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction( SIGPIPE, &act, NULL); /* 파이프 디스크립터 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
|
||||
sigaction( SIGHUP , &act, NULL); /* Process를 기동시킨 관리자의 로그아웃시 발생 시그널 */
|
||||
sigaction( SIGINT , &act, NULL); /* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
|
||||
sigaction( SIGQUIT, &act, NULL); /* 키보드에 의한 Abort 신호 처리 => ? */
|
||||
|
||||
// Child Process 인 Worker 프로세스 종료에 대한 처리기 설정.
|
||||
act.sa_handler = SigChldListener;
|
||||
sigaction( SIGCHLD, &act, NULL);
|
||||
|
||||
/* 각종 에러나 사용자의 종료 신호 처리 */
|
||||
act.sa_handler = SigTermListener;
|
||||
sigaction( SIGTERM, &act, NULL); /* kill -TERM 에 의한 프로세스 종료시 */
|
||||
|
||||
/* Core 관련 signal 처리 : 오류 처리 및 Debug(core dump) 목적 */
|
||||
act.sa_handler = SigCoreListener;
|
||||
sigaction( SIGILL , &act, NULL); /* Illegal instruction */
|
||||
sigaction( SIGFPE , &act, NULL); /* Erroneout arithmetic operation */
|
||||
sigaction( SIGBUS , &act, NULL); /* Access to undefined portion of a memory object */
|
||||
sigaction( SIGSEGV, &act, NULL); /* Invalid memory reference */
|
||||
sigaction( SIGSYS , &act, NULL); /* Bad System Call */
|
||||
sigaction( SIGXCPU, &act, NULL); /* CPU-time limit exceeded */
|
||||
sigaction( SIGXFSZ, &act, NULL); /* File-size limit exceeded */
|
||||
|
||||
sigemptyset(&set); /* 신호 처리기 처리 설정 위한 블록 해제 */
|
||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||
}
|
||||
|
||||
|
||||
/// @brief Listen 처리를 위한 Socket 생성 함수
|
||||
/// @param listenSocket [out] 생성된 Socket 정보를 저장하기 위한 참조변수
|
||||
/// @return 성공시 true, 오류발생시 false 반환.
|
||||
bool MakeListenSocket( int& listenSocket )
|
||||
{
|
||||
listenSocket = socket( AF_INET, SOCK_STREAM, 0 );
|
||||
if( listenSocket == -1 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Listen socket create failed.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
cerr << "[error] Process[" << PROG_NAME << "] Listen socket create failed.[" << strerror(errorNum) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_in listenSockAddr;
|
||||
socklen_t listenSockLen = 0;
|
||||
memset(&listenSockAddr, 0x00, sizeof(listenSockAddr));
|
||||
|
||||
listenSockAddr.sin_family = AF_INET;
|
||||
listenSockAddr.sin_port = htons( SVC_LISTEN_PORT ); // SVC_LISTEN_PORT : defined Makefile
|
||||
listenSockAddr.sin_addr.s_addr = htonl( INADDR_ANY );
|
||||
|
||||
listenSockLen = sizeof(listenSockAddr);
|
||||
|
||||
int result = 0;
|
||||
|
||||
// Socket Port Reuse Option Set
|
||||
int opt = 1;
|
||||
result = setsockopt( listenSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt) );
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Listen socket option[SO_REUSEADDR] set failed. [%d][%s]", errorNum, strerror(errorNum) );
|
||||
cerr << "[error] Process[" << PROG_NAME << "] Listen socket SO_REUSEADDR set failed.[" << strerror(errorNum) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep Alive Option set
|
||||
opt = 1;
|
||||
result = setsockopt( listenSocket, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt) );
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Listen socket option[SO_KEEPALIVE] set failed. [%d][%s]", errorNum, strerror(errorNum) );
|
||||
cerr << "[error] Process[" << PROG_NAME << "] Listen socket SO_KEEPALIVE set failed.[" << strerror(errorNum) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Non Block Mode Set
|
||||
/*
|
||||
int flags;
|
||||
if(flags = fcntl(listenSocket, F_GETFL, 0) >= 0)
|
||||
{
|
||||
flags |= O_NONBLOCK;
|
||||
if(fcntl(listenSocket, F_SETFL, flags) < 0)
|
||||
{
|
||||
int errorNum = errno;
|
||||
ERROR_LOG( "Listen socket non-block set failed.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
cerr << "[error] Process[" << PROG_NAME << "] Listen socket non-block set failed.[" << strerror(errorNum) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Socket Bind
|
||||
result = bind( listenSocket, (struct sockaddr *)&listenSockAddr, listenSockLen);
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Listen socket bind failed.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
cerr << "[error] Process[" << PROG_NAME << "] Listen socket bind failed.[" << strerror(errorNum) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Socket Listen
|
||||
result = listen( listenSocket, DEFAULT_ACCEPT_WAIT_COUNT );
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Listen socket bind failed.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
cerr << "[error] Process[" << PROG_NAME << "] Listen socket bind failed.[" << strerror(errorNum) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/***************************************************************************
|
||||
ftsd ( File TranSfer Daemon ) Header ( Listener.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/05
|
||||
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 __LISTENER_H__
|
||||
#define __LISTENER_H__
|
||||
|
||||
#include "../define_ftsd.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
|
||||
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
|
||||
bool IsCurrentProcessRun( void );
|
||||
|
||||
/// @brief std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
|
||||
/// @param str [in/out] Trim 할 문자열을 저장한 String 참조변수.
|
||||
/// @return void
|
||||
void Trim( std::string & str );
|
||||
|
||||
/// @brief Daemon Process 생성처리
|
||||
/// @return 0 인 경우 성공, -1 인 경우 오류발생
|
||||
int MakeDaemon( void );
|
||||
|
||||
/// @brief Worker Process 생성(fork) 처리 함수
|
||||
/// @param listenSocket [in] Listen Socket
|
||||
/// @return 생성 성공시 true, 실패시에는 false 를 반환.
|
||||
bool MakeProcessWorker( int listenSocket );
|
||||
|
||||
/// @brief Worker Process 재생성(fork) 처리 함수
|
||||
/// @param listenSocket [in] Listen Socket
|
||||
/// @return 생성 성공시 true, 실패시에는 false 를 반환.
|
||||
bool RemakeProcessWorker( int listenSocket );
|
||||
|
||||
/// @brief Listener 프로세스 signal 처리 설정을 위한 함수
|
||||
/// @return void
|
||||
void SetSignalListener( void );
|
||||
|
||||
/// @brief Listen 처리를 위한 Socket 생성 함수
|
||||
/// @param listenSocket [out] 생성된 Socket 정보를 저장하기 위한 참조변수
|
||||
/// @return 성공시 true, 오류발생시 false 반환.
|
||||
bool MakeListenSocket( int& listenSocket );
|
||||
|
||||
|
||||
/* Signal 처리를 위한 각 Signal Handler 함수는
|
||||
* 다른 Code 에서 Include 처리시 Static 관련 문제로 인해
|
||||
* 본 Header 에서 선언처리 하지 않음. cpp 에만 존재
|
||||
*/
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __LISTENER_H__ */
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#include "LocalIPCache.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
// IP Info save cache interval (sec)
|
||||
#define IPCACHE_TERM_SECOND 60
|
||||
|
||||
|
||||
// Constructor
|
||||
CLocalIPCache::CLocalIPCache( const string& szNICName )
|
||||
: m_szNICName( szNICName )
|
||||
{
|
||||
m_szLocalIP.clear();
|
||||
pthread_mutex_init(&m_mxLock, NULL);
|
||||
|
||||
LOG( LDBG, "Created CLocalIPCache object..");
|
||||
}
|
||||
|
||||
CLocalIPCache::~CLocalIPCache()
|
||||
{
|
||||
// 동작 중인 Thread 정지 처리.
|
||||
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 가 다른 Thread Handle 일 수 있으므로 업무 Flow 수정시 주의할 것
|
||||
pthread_cancel(m_threadHandle);
|
||||
|
||||
pthread_mutex_destroy(&m_mxLock);
|
||||
//LOG( LDBG, "Destroyed CLocalIPCache object..");
|
||||
}
|
||||
|
||||
bool CLocalIPCache::GetLocalIP(string& szLocalIP)
|
||||
{
|
||||
LOG( LDBG, "return IP is [%s]", m_szLocalIP.c_str());
|
||||
|
||||
pthread_mutex_lock(&m_mxLock);
|
||||
szLocalIP = m_szLocalIP;
|
||||
pthread_mutex_unlock(&m_mxLock);
|
||||
|
||||
// 유효한 IP인지를 확인 한다.
|
||||
if( (int)inet_addr(szLocalIP.c_str()) == -1 )
|
||||
{
|
||||
LOG( LERR, "[%s] is invalid IP.", szLocalIP.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLocalIPCache::GetIP(string& szLocalIP)
|
||||
{
|
||||
int fd = 0; // make new file descriptor to open socket
|
||||
int nResult = 0;
|
||||
|
||||
struct ifreq interface;
|
||||
struct sockaddr_in * pAddr = NULL;
|
||||
|
||||
// NEW 2010-11-30 huibong struct 초기화 코드 추가
|
||||
memset( &interface, 0x00, sizeof(struct ifreq) );
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0); // use IPv4
|
||||
if (fd < 0)
|
||||
{
|
||||
LOG( LERR, "socket(SOCK_DGRAM) create failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
interface.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(interface.ifr_name, m_szNICName.c_str(), IFNAMSIZ-1);
|
||||
|
||||
nResult = ioctl(fd, SIOCGIFADDR, &interface);
|
||||
if( nResult < 0 )
|
||||
{
|
||||
LOG( LERR, "ioctl error [%s][%d][%s]", m_szNICName.c_str(), errno, strerror(errno));
|
||||
close(fd);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
// CHG 2012-02-01 huibong
|
||||
// inet_ntoa() 함수가 static 메모리 사용으로 인해 Thread-safe 하지 않으므로..
|
||||
// inet_ntop() 함수로 교체 처리한다.
|
||||
|
||||
//szLocalIP = inet_ntoa(((struct sockaddr_in *)&interfaceReq.ifr_addr)->sin_addr);
|
||||
pAddr = (struct sockaddr_in *)&(interface.ifr_addr);
|
||||
char tempBuffer[INET_ADDRSTRLEN];
|
||||
memset(tempBuffer, 0x00, sizeof(tempBuffer));
|
||||
|
||||
if( inet_ntop( AF_INET, &(pAddr->sin_addr), tempBuffer, sizeof(tempBuffer)) != NULL )
|
||||
{
|
||||
szLocalIP = tempBuffer;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "inet_ntop error.[%d][%s]", errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
///@brief 이 함수에서 로컬IP 정보를 가져온다.
|
||||
void CLocalIPCache::Excute()
|
||||
{
|
||||
string szLocalIP;
|
||||
|
||||
if ( !GetIP(szLocalIP) )
|
||||
{
|
||||
LOG( LWAR, "Don't get ip from [%s]NIC. ", m_szNICName.c_str());
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&m_mxLock);
|
||||
m_szLocalIP = szLocalIP;
|
||||
pthread_mutex_unlock(&m_mxLock);
|
||||
}
|
||||
|
||||
void* CLocalIPCache::EntryPoint(void* arg)
|
||||
{
|
||||
CLocalIPCache* pObject = reinterpret_cast<CLocalIPCache *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while(1)
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Excute();
|
||||
pthread_testcancel();
|
||||
|
||||
sleep(IPCACHE_TERM_SECOND);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CLocalIPCache::Start()
|
||||
{
|
||||
if( m_szNICName.empty() )
|
||||
{
|
||||
LOG( LERR, "Thread create failed: NIC name is empty.");
|
||||
return false;
|
||||
}
|
||||
int nRet = ::pthread_create(&m_threadHandle, 0, CLocalIPCache::EntryPoint, this);
|
||||
|
||||
if( nRet )
|
||||
{
|
||||
LOG( LERR, "Thread create failed: errno:%d errmsg:%s", errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
LOG( LDBG, "Thread create succeed");
|
||||
sleep(0);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/***************************************************************************
|
||||
Local Ip Cache Class
|
||||
-----------------------------------------
|
||||
begin : 2010/03/31
|
||||
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 __LOCAL_IP_CACHE__
|
||||
#define __LOCAL_IP_CACHE__
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/// @brief CLocalIPCache
|
||||
/// CLocalIPCache는 생성자의 두 번째 param으로 입력한 NIC명의
|
||||
/// IP정보를 전달 해주는 클래스이다.
|
||||
/// 주요 인터페이스 함수는 쓰레드를 시작 하는 start()함수와
|
||||
/// IP를 얻어오는 GetLocalIP() 함수이다.
|
||||
///
|
||||
/// 내부적으로 Thread를 사용하고있다.
|
||||
class CLocalIPCache
|
||||
{
|
||||
public:
|
||||
/// @brief 생성자
|
||||
/// @param [in] szNICName 현재 클래스를 사용하여 IP정보를 얻을 NIC 장비의 이름을 입력한다.
|
||||
CLocalIPCache( const string& szNICName );
|
||||
~CLocalIPCache();
|
||||
|
||||
|
||||
/// @brief Thread를 생성하고, 이를 시작한다.
|
||||
/// @param none
|
||||
/// @return 성공은 return true 실패는 return false
|
||||
bool Start();
|
||||
|
||||
/// @brief 생성자의 두 번째 param으로 입력된 NIC 장비의 IP정보를 리턴한다.
|
||||
/// @param [out] szLocalIP 요청한 NIC에 해당하는 IP값을 전달한다.
|
||||
/// @return 성공은 return true, 실패하면 false.
|
||||
bool GetLocalIP(string& szLocalIP);
|
||||
|
||||
private:
|
||||
|
||||
// 입력받은 NIC 명.
|
||||
string m_szNICName;
|
||||
|
||||
// 해당 NIC의 ip를 입력받는다.
|
||||
string m_szLocalIP;
|
||||
|
||||
pthread_mutex_t m_mxLock;
|
||||
|
||||
// 쓰레드 핸들
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// 쓰레드 시작 루틴이다.
|
||||
static void* EntryPoint(void* arg);
|
||||
|
||||
// 해당 함수의 내용은 수정하지 말것.
|
||||
void Excute();
|
||||
|
||||
// m_szNICName 해당 NIC의 ip정보를 얻는 함수이다.
|
||||
bool GetIP(string& szLocalIP);
|
||||
};
|
||||
|
||||
#endif // __LOCAL_IP_CACHE__
|
||||
@@ -0,0 +1,74 @@
|
||||
#****************************************************************************
|
||||
# Makefile for ftsd ( File TranSfer Daemon)
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2010/02/25
|
||||
# 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.
|
||||
#*****************************************************************************
|
||||
|
||||
# Program info
|
||||
PROG_NAME = ftsd
|
||||
REVISION = 1534
|
||||
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
|
||||
|
||||
DEFAULT_CONFIG_FILE = /user/service/etc/fhs.conf
|
||||
SVC_LISTEN_PORT = 14001
|
||||
|
||||
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 =
|
||||
|
||||
# DEBUG or RELEASE Mode select
|
||||
#DFLAGS = -D_DEBUG_ -DPROG_NAME=\"$(PROG_NAME)\" -DPROG_VERSION=\"$(PROG_VERSION)\" -DSVC_LISTEN_PORT=$(SVC_LISTEN_PORT)
|
||||
DFLAGS = -DPROG_NAME=\"$(PROG_NAME)\" -DPROG_VERSION=\"$(PROG_VERSION)\" -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\"\
|
||||
-DSVC_LISTEN_PORT=$(SVC_LISTEN_PORT)
|
||||
|
||||
# Application Enviroment
|
||||
APP = $(PROG_NAME)
|
||||
|
||||
DIR_INCLUDE = -I../lib
|
||||
DIR_LIB = -L../lib
|
||||
|
||||
OBJ = GetConfig.o Listener.o LocalIPCache.o IPCache.o ProcessStatus.o ProperStorage.o FileHash.o TOTOChecksum.o SocketControl.o TargetFhsInfo.o FileSender.o Worker.o
|
||||
LIBS = ../lib/libInterCommon.a -lpthread
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
|
||||
all:$(APP)
|
||||
sync
|
||||
|
||||
%.o: %.cpp
|
||||
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
|
||||
|
||||
|
||||
$(PROG_NAME): $(OBJ)
|
||||
$(CC) $(LFLAGS) -o $@ $^ $(DFLAGS) $(DIR_LIB) $(LIBS)
|
||||
|
||||
|
||||
clean:
|
||||
-rm -f *.o core *.out *.log
|
||||
-rm -f $(APP)
|
||||
sync
|
||||
|
||||
|
||||
install : $(APP)
|
||||
-cp $(APP) $(INSTALL_BIN)/$(APP)
|
||||
sync
|
||||
|
||||
|
||||
# End of Makefile
|
||||
@@ -0,0 +1,209 @@
|
||||
#include <iostream>
|
||||
#include "ProcessStatus.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define MX_LOCK_SEND_CNT()\
|
||||
do \
|
||||
{\
|
||||
pthread_mutex_lock(&m_mx_nSendCount);\
|
||||
} while( 0 );
|
||||
|
||||
#define MX_UNLOCK_SEND_CNT()\
|
||||
do \
|
||||
{\
|
||||
pthread_mutex_unlock(&m_mx_nSendCount);\
|
||||
UpdateTitle();\
|
||||
} while( 0 );
|
||||
|
||||
#define MX_LOCK_RECV_CNT()\
|
||||
do \
|
||||
{\
|
||||
pthread_mutex_lock(&m_mx_nReceiveCount);\
|
||||
} while( 0 );
|
||||
|
||||
#define MX_UNLOCK_RECV_CNT()\
|
||||
do \
|
||||
{\
|
||||
pthread_mutex_unlock(&m_mx_nReceiveCount);\
|
||||
UpdateTitle();\
|
||||
} while( 0 );
|
||||
|
||||
#define MX_LOCK_TRAN_RATE()\
|
||||
do \
|
||||
{\
|
||||
pthread_mutex_lock(&m_mx_nTransferRate);\
|
||||
} while( 0 );
|
||||
|
||||
#define MX_UNLOCK_TRAN_RATE()\
|
||||
do \
|
||||
{\
|
||||
pthread_mutex_unlock(&m_mx_nTransferRate);\
|
||||
} while( 0 );
|
||||
|
||||
|
||||
// Constructor
|
||||
CProcessStatus::CProcessStatus()
|
||||
: m_nSendCount(0)
|
||||
, m_nReceiveCount(0)
|
||||
, m_nTransferRate(100)
|
||||
{
|
||||
// Initialize Mutext variable
|
||||
//m_mx_nSendCount = PTHREAD_MUTEX_INITIALIZER;
|
||||
//m_mx_nReceiveCount = PTHREAD_MUTEX_INITIALIZER;
|
||||
//m_mx_nTransferRate = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
pthread_mutex_init( &m_mx_nSendCount, NULL );
|
||||
pthread_mutex_init( &m_mx_nReceiveCount, NULL );
|
||||
pthread_mutex_init( &m_mx_nTransferRate, NULL );
|
||||
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CProcessStatus::~CProcessStatus()
|
||||
{
|
||||
// Destory Mutext variable
|
||||
pthread_mutex_destroy(&m_mx_nSendCount);
|
||||
pthread_mutex_destroy(&m_mx_nReceiveCount);
|
||||
pthread_mutex_destroy(&m_mx_nTransferRate);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void CProcessStatus::UpdateTitle()
|
||||
{
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
setproctitle("Worker [S:%3d R:%3d N:%3d]", m_nSendCount, m_nReceiveCount, m_nTransferRate );
|
||||
#endif
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
int CProcessStatus::GetSendCount(void)
|
||||
{
|
||||
int nValue = 0;
|
||||
|
||||
MX_LOCK_SEND_CNT();
|
||||
nValue = m_nSendCount;
|
||||
MX_UNLOCK_SEND_CNT();
|
||||
|
||||
return nValue;
|
||||
}
|
||||
|
||||
bool CProcessStatus::SetSendCount(const int nSendCount)
|
||||
{
|
||||
// 입력값이 0 보다 작은 경우 false 반환.
|
||||
if( nSendCount < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MX_LOCK_SEND_CNT();
|
||||
m_nSendCount = nSendCount;
|
||||
MX_UNLOCK_SEND_CNT();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CProcessStatus::AddSendCount()
|
||||
{
|
||||
MX_LOCK_SEND_CNT();
|
||||
++m_nSendCount;
|
||||
MX_UNLOCK_SEND_CNT();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CProcessStatus::SubtractSendCount()
|
||||
{
|
||||
MX_LOCK_SEND_CNT();
|
||||
|
||||
--m_nSendCount;
|
||||
if( m_nSendCount < 0 )
|
||||
m_nSendCount = 0;
|
||||
|
||||
MX_UNLOCK_SEND_CNT();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int CProcessStatus::GetReceiveCount(void)
|
||||
{
|
||||
int nValue = 0;
|
||||
|
||||
MX_LOCK_RECV_CNT();
|
||||
nValue = m_nReceiveCount;
|
||||
MX_UNLOCK_RECV_CNT();
|
||||
|
||||
return nValue;
|
||||
}
|
||||
|
||||
bool CProcessStatus::SetReceiveCount(const int nReceiveCount)
|
||||
{
|
||||
if( nReceiveCount < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MX_LOCK_RECV_CNT();
|
||||
m_nReceiveCount = nReceiveCount;
|
||||
MX_UNLOCK_RECV_CNT();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CProcessStatus::AddReceiveCount()
|
||||
{
|
||||
MX_LOCK_RECV_CNT();
|
||||
++m_nReceiveCount;
|
||||
MX_UNLOCK_RECV_CNT();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CProcessStatus::SubtractReceiveCount()
|
||||
{
|
||||
MX_LOCK_RECV_CNT();
|
||||
|
||||
--m_nReceiveCount;
|
||||
if( m_nReceiveCount < 0 )
|
||||
m_nReceiveCount = 0;
|
||||
|
||||
MX_UNLOCK_RECV_CNT();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CProcessStatus::GetTransferRate()
|
||||
{
|
||||
int nValue = 0;
|
||||
|
||||
MX_LOCK_TRAN_RATE();
|
||||
nValue = m_nTransferRate;
|
||||
MX_UNLOCK_TRAN_RATE();
|
||||
|
||||
return nValue;
|
||||
}
|
||||
|
||||
bool CProcessStatus::SetTransferRate(const int nTransferRate)
|
||||
{
|
||||
if ( nTransferRate < 0 || nTransferRate > 100 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MX_LOCK_TRAN_RATE();
|
||||
m_nTransferRate = nTransferRate;
|
||||
MX_UNLOCK_TRAN_RATE();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/***************************************************************************
|
||||
Process Status class
|
||||
-----------------------------------------
|
||||
begin : 2010/03/05
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : tael@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.
|
||||
***************************************************************************/
|
||||
// TODO: comment 보완
|
||||
|
||||
#ifndef __PROCESS_STATUS_H__
|
||||
#define __PROCESS_STATUS_H__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/// @brief CProcessStatus class. 현재 송신/수신 중인 갯수를 관리하는 클래스.
|
||||
class CProcessStatus
|
||||
{
|
||||
public :
|
||||
CProcessStatus();
|
||||
~CProcessStatus();
|
||||
|
||||
/// @brief 현재 설정된 Send Count 값을 가져온다.
|
||||
/// @return 성공은 SendCount, 그 외에는 음수 값
|
||||
int GetSendCount(void);
|
||||
|
||||
/// @brief Send Count를 설정한다.
|
||||
/// @param nSendCount [in] Send Count를 설정한다.
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool SetSendCount(const int nSendCount); // TODO: NOT USED, remove it
|
||||
|
||||
/// @brief
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool AddSendCount(void);
|
||||
/// @brief
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool SubtractSendCount(void);
|
||||
|
||||
/// @brief 현재 설정된 ReceiveCount 값을 가져온다.
|
||||
/// @return 성공은 ReceiveCount, 그 외에는 음수 값
|
||||
int GetReceiveCount(void);
|
||||
|
||||
/// @brief 현재 설정된 Receive Count 값을 가져온다.
|
||||
/// @param nReceiveCount [in] Receive Count를 설정한다.
|
||||
/// @return 성공은 m_nReceiveCount, 그 외에는 음수 값
|
||||
bool SetReceiveCount(const int nReceiveCount);// TODO: NOT USED, remove it
|
||||
|
||||
/// @brief
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool AddReceiveCount(void);
|
||||
|
||||
/// @brief
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool SubtractReceiveCount(void);
|
||||
|
||||
/// @brief 현재 설정된 TransferRate 값을 가져온다. 0~100까지의 값을 가진다.
|
||||
/// @return 성공은 m_nTransferRate, 그 외에는 음수 값
|
||||
int GetTransferRate(void);
|
||||
|
||||
/// @brief 현재 설정된 Transfer Rate 값을 설정한다. ( 0~100 )
|
||||
/// @param nTransferRate [in] 설정된 전송률을 설정한다. ( 0~100 )
|
||||
/// @return 성공은 m_nReceiveCount, 그 외에는 음수 값
|
||||
bool SetTransferRate(const int nTransferRate);
|
||||
|
||||
private :
|
||||
/// @brief 현재 프로세스 타이틀을 업데이트 한다. (SndCnt,RcvCnt정보를 포함한다.) (FreeBSD Only)
|
||||
void UpdateTitle(void);
|
||||
|
||||
/// @brief 현재 전송중인 개수를 저장. 0보다 큰 값을 가지게 된다.
|
||||
int m_nSendCount;
|
||||
|
||||
/// @brief 현재 수신중인 개수를 저장. 0보다 큰 값을 가지게 된다.
|
||||
int m_nReceiveCount;
|
||||
|
||||
/// @brief 현재 설정된 전송률을 저장. 정수 단위로 0~100까지의 값을 가지게 된다.
|
||||
int m_nTransferRate;
|
||||
|
||||
pthread_mutex_t m_mx_nSendCount ;
|
||||
pthread_mutex_t m_mx_nReceiveCount;
|
||||
pthread_mutex_t m_mx_nTransferRate;
|
||||
|
||||
}; // ~class CProcessStatus
|
||||
|
||||
#endif // __PROCESS_STATUS_H__
|
||||
@@ -0,0 +1,585 @@
|
||||
#include "ProperStorage.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/param.h>
|
||||
#include <sys/ucred.h>
|
||||
#include <sys/mount.h>
|
||||
#else // Linux
|
||||
#include <mntent.h>
|
||||
#include <sys/vfs.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define LIMIT_STORAGE_SIZE 10737418240LL // 최소 Storage 용량.
|
||||
#define DISABLE_FLAG ".disable" // Mount disable 플래그
|
||||
|
||||
|
||||
// 생성자
|
||||
CProperStorage::CProperStorage( const std::string & strStorageRoot, const int nCheckInterval )
|
||||
{
|
||||
// 멤버 변수 초기화
|
||||
pthread_mutex_init( &m_mutexLock, NULL ); // Mutex Lock init.
|
||||
m_nCheckInterval = nCheckInterval;
|
||||
m_strProperPath.clear();
|
||||
m_nAvailableSize = 0;
|
||||
|
||||
// 최상위 경로값에서 마지막 문자가 '/' 인 경우.. 제거 처리한다.
|
||||
// - 업무 Flow 관련 경로 처리 문제 사전 해결
|
||||
// - 시스템에서 추출된 mount 정보와 비교 처리시 정상 비교가 될 수 있도록..
|
||||
if( strStorageRoot.length() > 1 )
|
||||
{
|
||||
if( strStorageRoot.at( strStorageRoot.length() -1 ) == '/' )
|
||||
{
|
||||
m_strStorageRoot = strStorageRoot.substr( 0, strStorageRoot.length() -1 );
|
||||
}
|
||||
else
|
||||
m_strStorageRoot = strStorageRoot;
|
||||
}
|
||||
else
|
||||
m_strStorageRoot = strStorageRoot;
|
||||
}
|
||||
|
||||
// 소멸자
|
||||
CProperStorage::~CProperStorage()
|
||||
{
|
||||
// 동작 중인 Thread 정지 처리.
|
||||
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 가 다른 Thread Handle 일 수 있으므로 업무 Flow 수정시 주의할 것
|
||||
pthread_cancel( m_threadHandle );
|
||||
|
||||
// Mutex Lock 제거
|
||||
pthread_mutex_destroy( &m_mutexLock );
|
||||
}
|
||||
|
||||
// Content Write 관련 최적 Mount 경로를 반환한다.
|
||||
bool CProperStorage::GetProperPath( std::string& strProperPath )
|
||||
{
|
||||
bool bResult;
|
||||
|
||||
// Thread 와 동시 접근을 제어하기 위해 Lock 처리
|
||||
pthread_mutex_lock( &m_mutexLock );
|
||||
if( m_strProperPath.empty() == true )
|
||||
{
|
||||
// 최적 경로를 추출하지 못한 경우...
|
||||
bResult = false;
|
||||
LOG( LERR, "proper mount path empty. return false.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 최적 경로가 존재하는 경우..
|
||||
strProperPath = m_strProperPath;
|
||||
bResult = true;
|
||||
LOG( LDBG, "proper mount path return. [%s]", strProperPath.c_str() );
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &m_mutexLock );
|
||||
return bResult;
|
||||
}
|
||||
|
||||
|
||||
// Content Write 관련 최적 Mount 경로 및 사용 가능 Free Size 를 반환한다.
|
||||
bool CProperStorage::GetProperPath( std::string& strProperPath, unsigned long long & nAvailableSzie )
|
||||
{
|
||||
bool bResult;
|
||||
|
||||
// Thread 와 동시 접근을 제어하기 위해 Lock 처리
|
||||
pthread_mutex_lock( &m_mutexLock );
|
||||
if( m_strProperPath.empty() == true )
|
||||
{
|
||||
// 최적 경로를 추출하지 못한 경우...
|
||||
nAvailableSzie = 0;
|
||||
bResult = false;
|
||||
|
||||
LOG( LERR, "proper mount path empty. return false.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 최적 경로가 존재하는 경우..
|
||||
strProperPath = m_strProperPath;
|
||||
nAvailableSzie = m_nAvailableSize;
|
||||
bResult = true;
|
||||
|
||||
LOG( LDBG, "proper mount path return. [%s] free[%llu Byte]", strProperPath.c_str(), nAvailableSzie );
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &m_mutexLock );
|
||||
return bResult;
|
||||
}
|
||||
|
||||
|
||||
// Content Write 관련 최적 Mount 경로의 사용 가능 Free Size 를 반환한다.
|
||||
// - GetProperPath() 함수를 통해 경로 정보 수신 후 본 함수를 호출할 경우...
|
||||
// - Thread 에 의해 경로 정보 및 Free 용량 정보가 변경되어 다른 경로에 대한 Free 용량 정보가 전달될 수 있으니... 사용시 주의한다.
|
||||
void CProperStorage::GetAvailableSize( unsigned long long & nAvailableSzie )
|
||||
{
|
||||
// Thread 와 동시 접근을 제어하기 위해 Lock 처리
|
||||
pthread_mutex_lock( &m_mutexLock );
|
||||
|
||||
nAvailableSzie = m_nAvailableSize;
|
||||
LOG( LDBG, "proper mount free size [%llu] return. path[%s]", nAvailableSzie, m_strProperPath.c_str() );
|
||||
|
||||
pthread_mutex_unlock( &m_mutexLock );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Thread 를 생성하여 Mount 정보에 대한 검사를 주기적으로 수행한다.
|
||||
bool CProperStorage::Start()
|
||||
{
|
||||
// 생성자를 통해 전달받은 최상위 경로 정보가 유효하지 않은 경우..
|
||||
if( m_strStorageRoot.empty() == true )
|
||||
{
|
||||
LOG( LERR, "storage root path empty. ");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 생성자를 통해 전달받은 최상위 경로 정보가 '/' 정보인 경우에도 유효하지 않으므로 오류로 처리한다.
|
||||
if( strcmp( m_strStorageRoot.c_str(), "/") == 0 )
|
||||
{
|
||||
LOG( LERR, "storage root path not vaild. [%s]", m_strStorageRoot.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Thread 를 생성 시킨다.
|
||||
int nResult = pthread_create( &m_threadHandle, 0, CProperStorage::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// Thread 생성 실패시...
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "thread create failed.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Thread 함수
|
||||
void * CProperStorage::threadFunc( void * arg )
|
||||
{
|
||||
CProperStorage * pObject = reinterpret_cast<CProperStorage *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while(1)
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 시간동안 sleep..
|
||||
sleep( pObject->m_nCheckInterval );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// sort 처리를 수행하기 위한 함수.
|
||||
bool SortMountAvailableSize( struct systme_mount_info fisrt, struct systme_mount_info second)
|
||||
{
|
||||
// first 의 available_byte 용량 순으로 정렬.
|
||||
return ( fisrt.available_byte > second.available_byte ) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
// 실제 시스템 Disk Mount 정보를 조회하여... 최적 Mount 경로 정보를 추출하기 위한 함수.
|
||||
// 본 함수 Flow 로직은 코드를 단순하게 하여 라인 수를 줄여 compact 하게 할 수 있으나... 함수 Flow 이해가 어렵게 된다.
|
||||
// 따라서.. 중복되는 상황이 나오더라도.. 업무 Flow 순서에 명확하도록 개발한다.
|
||||
void CProperStorage::Execute()
|
||||
{
|
||||
// Start() 함수에서 최상위 경로 인자값에 대한 검사를 했으므로.... 최상위 경로값을 정상이라고 판단하고 검사를 수행한다.
|
||||
// 최상위 경로값은 다음과 같이 세팅될 수 있다.
|
||||
// - /stg , /stg/, /stg/node0 , /stg/node0/
|
||||
// - /user2 , /user2/, /user2/dav_storage, /user2/dav_storage/
|
||||
|
||||
// 1. 최상위 경로값에서 마지막 문자가 '/' 인 경우.. 제거 처리한다.
|
||||
// - 업무 Flow 관련 경로 처리 문제 사전 해결
|
||||
// - 시스템에서 추출된 mount 정보와 비교 처리시 정상 비교가 될 수 있도록..
|
||||
// => 본 처리는 생성자에서 처리를 수행하므로.. pass....
|
||||
|
||||
// 2. 장비의 Mount 정보 와 available 용량 정보를 추출한다.
|
||||
if( GetSystmeMountInfo() == false )
|
||||
{
|
||||
// 시스템의 Mount 정보 추출 실패 또는 Mount 정보가 존재하지 않는 경우...
|
||||
// Mount 정보 추출 실패로 인해 최적 경로 정보를 invalid 정보로 설정 처리한다.
|
||||
SetProperPathError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Mount 정보 저장 관련 임시 객체
|
||||
struct systme_mount_info stMountInfo;
|
||||
|
||||
// 3. 수정된 최상위 경로값과 동일한 장비 Mount 정보가 존재하는지 비교한다.
|
||||
// - 비교시 문자열 Full 비교 수행.. ( /stg/node0 등으로 설정된 경우를 위해 )
|
||||
|
||||
// 각 조건에 맞아 추출된 Mount 정보를 저장하기 위한 임시 변수.
|
||||
std::vector<struct systme_mount_info> vecTempMount;
|
||||
std::vector<struct systme_mount_info>::iterator it;
|
||||
|
||||
//_LOG( LDBG, "Worker[%d]::ProperStorage: mount full scan start. path[%s]", getpid(), m_strStorageRoot.c_str() );
|
||||
|
||||
for( it = m_vecMount.begin(); it < m_vecMount.end(); it++ )
|
||||
{
|
||||
if( strcmp( m_strStorageRoot.c_str(), it->mount_name.c_str() ) == 0 )
|
||||
{
|
||||
stMountInfo.mount_name = it->mount_name;
|
||||
stMountInfo.available_byte = it->available_byte;
|
||||
|
||||
vecTempMount.push_back( stMountInfo );
|
||||
|
||||
_LOG( LDBG, "Worker[%d]::ProperStorage: mount name full scan. [%s] available byte[%llu]"
|
||||
, getpid(), stMountInfo.mount_name.c_str(), stMountInfo.available_byte );
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 만약 위의 3번 항목에서 Mount 장비를 찾지 못한 경우....
|
||||
// - conf 상에 설정된 경로 정보가 실제 Mount 정보를 나타내는 것이 아니다.
|
||||
// - 수정된 최상위 경로부터 부분 비교를 시작해서 없을 경우.. sub path 부분을 제외한 후 다시 장비 Mount 정보와 비교한다.
|
||||
// - 장비 mount 정보를 찾을 때까지 본 비교를 반복하여 수행한다.
|
||||
if( vecTempMount.empty() == true )
|
||||
{
|
||||
// 최초 비교시에는 원래 정보를 가지고 부분 비교처리.. (/stg 등으로 설정된 경우를 위해)
|
||||
std::string strPreviousPath = m_strStorageRoot;
|
||||
std::string strCurrentPath = m_strStorageRoot;
|
||||
|
||||
do
|
||||
{
|
||||
//_LOG( LDBG, "Worker[%d]::ProperStorage: mount partical scan start.. path[%s]", getpid(), strCurrentPath.c_str() );
|
||||
|
||||
// 수정된 경로 정보만을 가지고 부분 비교를 수행....
|
||||
for( it = m_vecMount.begin(); it < m_vecMount.end(); it++ )
|
||||
{
|
||||
// 부분 비교 처리
|
||||
if( strncmp( strCurrentPath.c_str(), it->mount_name.c_str(), strlen(strCurrentPath.c_str()) ) == 0 )
|
||||
{
|
||||
stMountInfo.mount_name = it->mount_name;
|
||||
stMountInfo.available_byte = it->available_byte;
|
||||
|
||||
vecTempMount.push_back( stMountInfo );
|
||||
|
||||
_LOG( LDBG, "Worker[%d]::ProperStorage: mount partical scan. path[%s], mount[%s] available byte[%llu]"
|
||||
, getpid(), strCurrentPath.c_str(), stMountInfo.mount_name.c_str(), stMountInfo.available_byte );
|
||||
}
|
||||
}
|
||||
|
||||
// 위에서 정보 추출이 실패한 경우..상위 Path 정보를 추출한다. ( /user2/dav_storage, /stg/node0/aa/bb 등으로 설정된 경우를 위해 )
|
||||
if( vecTempMount.empty() == true )
|
||||
{
|
||||
GetUpperPath( strPreviousPath, strCurrentPath );
|
||||
if( strCurrentPath.empty() == true )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
strPreviousPath = strCurrentPath;
|
||||
}
|
||||
|
||||
} while( vecTempMount.empty() == true );
|
||||
}
|
||||
|
||||
// 5. 3,4 항목에서 장비 mount 를 찾은 경우....
|
||||
//
|
||||
// 5.1 1개 Mount 만 검색된 경우...
|
||||
// - 최종 마운트 경로 정보는 1 항목에서 수정된 경로이며.....
|
||||
// - 여유용량이 임계치를 이상이면 정상, 임계치 미만이면 오류 처리한다.
|
||||
// - 1개 이므로.. mount 에 대한 disable 검사는 수행하지 않는다. (왜냐면 소용 없으므로... )
|
||||
//
|
||||
// 5.2 다중 Mount 가 검색된 경우...
|
||||
// - 각 mount 에 대한 여유용량 임계치를 검사한다.
|
||||
// 만약 여유용량이 임계치 미만이면 목록에서 제거한다.
|
||||
// - 수정된 mount 목록에서 각 Mount 경로에 대한 disable 검사를 수행한다.
|
||||
// - 최종... disable 되지 않고.. 여유 용량이 가장 많은 mount 를 선택한다.
|
||||
// 만약 전부 disable 된 경우... disable 을 무시하고 여유용량이 가장 많은 mount 를 선택한다.
|
||||
// - 최종 마운트 경로 정보는 시스템에서 추출된 Mount 경로를 사용한다. ( 5.1 과는 다른 선택임 )
|
||||
|
||||
if( vecTempMount.empty() == true )
|
||||
{
|
||||
// 적합한 Mount 경로를 찾지 못한 경우...
|
||||
// Mount 정보 추출 실패로 인해 최적 경로 정보를 invalid 정보로 설정 처리한다.
|
||||
SetProperPathError();
|
||||
LOG( LERR, "proper mount path find failed. conf path[%s]", m_strStorageRoot.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// 시스템에서 1개 Mount 정보만 검색된 경우.
|
||||
if( vecTempMount.size() == 1 )
|
||||
{
|
||||
// 여유용량에 대한 임계치를 검사 후...
|
||||
// 최적 Mount 경로 정보는 운영자가 설정한 경로 정보로 설정한다.. (/user2/dav_storage , /stg/node0, /stg/node0/aa/bb 등의 경우)
|
||||
if( vecTempMount[0].available_byte < LIMIT_STORAGE_SIZE )
|
||||
{
|
||||
LOG( LERR, "proper mount not found. mount[%s] available size[%llu] limit[%llu]"
|
||||
, m_strStorageRoot.c_str(), vecTempMount[0].available_byte, LIMIT_STORAGE_SIZE );
|
||||
|
||||
SetProperPathError();
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHG 2015-03-27 huibong
|
||||
// 설정값이 /stg 이고.. mount 정보가 1개 /stg/node0 인 경우...
|
||||
// 아래 로직에 의해 경로값이 설정값인 /stg 가 선택되는 문제 발생. ( #21917 )
|
||||
// 이를 해결하기 위해.. 설정값과 추출된 mount 이름 중 길이가 큰 값을 사용하도록 수정 처리한다.
|
||||
// SetProperPath( m_strStorageRoot, vecTempMount[0].available_byte );
|
||||
|
||||
if( m_strStorageRoot.size() > vecTempMount[0].mount_name.size() )
|
||||
SetProperPath( m_strStorageRoot, vecTempMount[0].available_byte );
|
||||
else
|
||||
SetProperPath( vecTempMount[0].mount_name, vecTempMount[0].available_byte );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 시스템에서 1개 초과 Mount 정보만 검색된 경우. -> 시스템에서 추출된 Mount 경로를 최적 Mount 경로를 사용한다.
|
||||
// 여유용량에 대한 임계치를 검사 후... -> 임계치 이하 항목은 제거..
|
||||
// 여유 용량이 가장 많은 Mount 순으로 정렬한 후...
|
||||
// Mount disable 검사....
|
||||
// 만약 전체 mount disable 되어 있다면... 가장 용량이 많은 Mount 선택 처리..
|
||||
|
||||
// 임계치 이상의 여유 공간을 가진 Mount 정보를 임시로 저장할 Vector.
|
||||
std::vector<struct systme_mount_info> vecAvailMount;
|
||||
|
||||
for( it = vecTempMount.begin(); it < vecTempMount.end(); it++ )
|
||||
{
|
||||
// 여유 용량에 대한 임계치 검사.
|
||||
if( it->available_byte >= LIMIT_STORAGE_SIZE )
|
||||
{
|
||||
stMountInfo.mount_name = it->mount_name;
|
||||
stMountInfo.available_byte = it->available_byte;
|
||||
|
||||
vecAvailMount.push_back( stMountInfo );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "mount available size not enough. mount[%s] available size[%llu] limit[%llu]"
|
||||
, it->mount_name.c_str(), it->available_byte, LIMIT_STORAGE_SIZE );
|
||||
}
|
||||
}
|
||||
|
||||
// 임계치 이상의 용량을 가진 Mount 가 없는 경우...
|
||||
if( vecAvailMount.empty() == true )
|
||||
{
|
||||
LOG( LERR, "proper mount not found. mount available size not enough. limit[%llu]", LIMIT_STORAGE_SIZE );
|
||||
SetProperPathError();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 최소 임계치 이상의 용량을 가진 Mount 가 존재하는 경우..
|
||||
// Available 용량이 큰 순서대로 sort 처리...
|
||||
// 루프를 돌며서 각 Mount 별로 disable 검사.. 만약 disable 처리가 안된 경우. 해당 Mount 를 Proper Mount 롤 선정.
|
||||
std::sort( vecAvailMount.begin(), vecAvailMount.end(), SortMountAvailableSize );
|
||||
|
||||
for( it = vecAvailMount.begin(); it < vecAvailMount.end(); it++ )
|
||||
{
|
||||
//_LOG( LDBG, "ProperStorage: Sort result. mount[%s] available size[%llu]" , it->mount_name.c_str(), it->available_byte );
|
||||
|
||||
// Mount 별로 Mount disable 되어 있는지 검사...
|
||||
if( CheckMountDisable( it->mount_name ) == false )
|
||||
{
|
||||
SetProperPath( it->mount_name, it->available_byte );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 위의 루프에서 Mount 가 전부 disable 되어 있어 적합한 Mount 를 찾지 못한 경우...
|
||||
// 용량별로 sort 되어 있으므로.. 첫번째 Mount 를 선정한다.
|
||||
_LOG( LWAR, "Worker[%d]::ProperStorage: all mount disable. forced selected by available size order.[%s] size[%llu]"
|
||||
, getpid(), vecAvailMount[0].mount_name.c_str(), vecAvailMount[0].available_byte );
|
||||
|
||||
SetProperPath( vecAvailMount[0].mount_name, vecAvailMount[0].available_byte );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// FHS 장비의 Mount 정보를 추출하여... 멤버 변수인 m_vecMount 에 저장한다.
|
||||
// return true : Mount 정보 정상 추출시...
|
||||
// false : Mount 정보 추출 실패 또는 추출된 Mount 정보가 없는 경우...
|
||||
bool CProperStorage::GetSystmeMountInfo()
|
||||
{
|
||||
// 추출된 Mount 정보 저장 객체 초기화.
|
||||
struct systme_mount_info stMountInfo;
|
||||
m_vecMount.clear();
|
||||
|
||||
// statfs 구조체 정보.
|
||||
// struct statfs {
|
||||
// char f_mntonname[88]; /* directory on which mounted */
|
||||
// int64_t f_bavail; /* free blocks avail to non-superuser */
|
||||
// uint64_t f_bsize; /* filesystem fragment size */
|
||||
// ..
|
||||
// }
|
||||
// => uint64_t = unsigned long long => 0 ~ 18,446,744,073,709,551,615 범위 가짐.
|
||||
|
||||
// FreeBSD 에서만.. getmntinfo 함수를 지원..
|
||||
// 향후 Linux Migration 을 대비하기 위해 OS 별 코드 추가한다.
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
// Mount 정보를 추출하기 위한 변수 초기화.
|
||||
int nMountCount = 0;
|
||||
struct statfs * pStatFsBuf = NULL;
|
||||
|
||||
// Mount 정보 추출...
|
||||
// getmntinfo 특성상.. 할당된 메모리를 application 에서 해제시키면 안되므로 주의할 것.
|
||||
nMountCount = getmntinfo( &pStatFsBuf, MNT_WAIT );
|
||||
if( nMountCount == 0 )
|
||||
{
|
||||
// Mount 정보 추출 실패시.. 오류 처리한다.
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "system getmntinfo function return failed. [%d][%s]", errorNum, strerror(errorNum) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for( int i = 0 ; i < nMountCount ; i++ )
|
||||
{
|
||||
stMountInfo.mount_name = pStatFsBuf[i].f_mntonname;
|
||||
stMountInfo.available_byte = pStatFsBuf[i].f_bavail * (unsigned long long)pStatFsBuf[i].f_bsize ;
|
||||
|
||||
m_vecMount.push_back( stMountInfo );
|
||||
|
||||
//LOG( LDBG, "system mount name[%s] available byte[%llu]", stMountInfo.mount_name.c_str(), stMountInfo.available_byte );
|
||||
|
||||
// 시스템에서 Mount 정보 추출시 유효 용량에 대한 임계치 검사를 수행할 경우....
|
||||
// 아래 코드에서 운영자가 설정한 Mount 정보 추출이 실패.. 잘못된 경로상에 Content 저장이 발생할 수 있으므로.. 임계치 검사를 수해하지 않는다.
|
||||
}
|
||||
|
||||
#else // Linux 인 경우...
|
||||
|
||||
FILE * fp = NULL;
|
||||
|
||||
struct mntent stMnt;
|
||||
struct mntent * pMnt;
|
||||
char tempBuf[512];
|
||||
|
||||
fp = setmntent( "/etc/mtab", "r");
|
||||
if( fp != NULL )
|
||||
{
|
||||
while( (pMnt = getmntent_r( fp, &stMnt, tempBuf, sizeof(tempBuf))) != NULL )
|
||||
{
|
||||
struct statfs statFsBuf;
|
||||
|
||||
if( (stMnt.mnt_dir != NULL) && (statfs( stMnt.mnt_dir, &statFsBuf ) == 0 ) )
|
||||
{
|
||||
stMountInfo.mount_name = stMnt.mnt_dir;
|
||||
stMountInfo.available_byte = statFsBuf.f_bavail * (unsigned long long)statFsBuf.f_bsize ;
|
||||
|
||||
m_vecMount.push_back( stMountInfo );
|
||||
|
||||
//LOG( LDBG, "system mount name[%s] available byte[%llu]", stMountInfo.mount_name.c_str(), stMountInfo.available_byte );
|
||||
}
|
||||
}
|
||||
|
||||
// open 된 fp close 처리.
|
||||
endmntent( fp );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// 추출된 결과 확인...
|
||||
if( m_vecMount.empty() == true )
|
||||
{
|
||||
// 추출된 system Mount 정보가 없는 경우..
|
||||
LOG( LERR, "system mount info empty. check mount info." );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 인자로 전달받은 Mount 에 대해 disable flag 가 등록되어 있는지 검사
|
||||
// true : mount disable flag 가 걸려 있는 경우.
|
||||
// false : mount disable flag 가 없는 경우..
|
||||
bool CProperStorage::CheckMountDisable( const std::string & strMountName)
|
||||
{
|
||||
char szDisableName[512];
|
||||
struct stat flagStat;
|
||||
|
||||
sprintf( szDisableName, "%s%s", strMountName.c_str(), DISABLE_FLAG );
|
||||
|
||||
// Mount disable flag 가 존재하고....
|
||||
if( lstat( szDisableName, &flagStat) == 0 )
|
||||
{
|
||||
// 해당 파일이 정규 파일인 경우.. ( 폴더, 링크 등은 제외 처리 )
|
||||
if( S_ISREG(flagStat.st_mode) != 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 전달받은 경로로부터.. 상위 Path 정보를 추출하여 전달한다.
|
||||
// 오류 발생시 [out] 값인 strUpperPath 가 empty 된다.
|
||||
void CProperStorage::GetUpperPath( const std::string & strSourcePath, std::string & strUpperPath )
|
||||
{
|
||||
// 인자로 전달되는 strSourcePath 끝에.. '/' 가 이미 제거되었다고 판단하고 작업을 수행한다.
|
||||
std::string::size_type pos = strSourcePath.rfind( '/' );
|
||||
if( pos == std::string::npos )
|
||||
{
|
||||
// 찾지 못한 경우...
|
||||
// out 값을 empty 처리시킨다.
|
||||
LOG( LNOT, "upper path not found. source[%s]", strSourcePath.c_str());
|
||||
strUpperPath.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 찾은 경우..
|
||||
strUpperPath = strSourcePath.substr(0, pos);
|
||||
|
||||
// 추출된 경로가... 최상위 '/' 인 경우.. 유효하지 않으므로 오류로 처리한다.
|
||||
if( strcmp( strUpperPath.c_str(), "/") == 0 )
|
||||
{
|
||||
LOG( LNOT, "upper path not vaild that root path. [%s]", strUpperPath.c_str() );
|
||||
strUpperPath.clear();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Content Write 관련 최적 Mount 경로 및 사용 가능 Free Size 를 설정한다.
|
||||
void CProperStorage::SetProperPath( std::string& strProperPath, unsigned long long & nAvailableSzie )
|
||||
{
|
||||
// Thread 와 동시 접근을 제어하기 위해 Lock 처리
|
||||
pthread_mutex_lock( &m_mutexLock );
|
||||
|
||||
m_strProperPath = strProperPath;
|
||||
m_nAvailableSize = nAvailableSzie;
|
||||
|
||||
pthread_mutex_unlock( &m_mutexLock );
|
||||
|
||||
_LOG( LDBG, "Worker[%d]::ProperStorage: proper path set. [%s] free[%llu]byte", getpid(), m_strProperPath.c_str(), m_nAvailableSize );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Content Write 관련 최적 Mount 경로 및 사용 가능 Free Size 추출 실패시..
|
||||
void CProperStorage::SetProperPathError()
|
||||
{
|
||||
// Thread 와 동시 접근을 제어하기 위해 Lock 처리
|
||||
pthread_mutex_lock( &m_mutexLock );
|
||||
|
||||
m_strProperPath.clear();
|
||||
m_nAvailableSize = 0;
|
||||
|
||||
pthread_mutex_unlock( &m_mutexLock );
|
||||
|
||||
LOG( LERR, "Worker[%d]: proper mount path set invalid value.", getpid() );
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/***************************************************************************
|
||||
Proper storage info collect thread class
|
||||
-----------------------------------------
|
||||
begin : 2014/02/27
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : solbox dev team
|
||||
email : huibong@solbox.com
|
||||
version : 3.3.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 __PROPER_STORAGE_H__
|
||||
#define __PROPER_STORAGE_H__
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
// 시스템에서 추출된 Mount 정보를 저장하기 위한 struct...
|
||||
struct systme_mount_info {
|
||||
|
||||
std::string mount_name; // Mount Name..
|
||||
|
||||
unsigned long long available_byte; // available byte
|
||||
};
|
||||
|
||||
|
||||
/// @brief CProperStorage
|
||||
/// 본 클래스는 Thread 클래스로서
|
||||
/// 장비에 mount 된 경로 정보 중... 가장 Free 용량이 많은 Mount 경로 정보를 반환한다.
|
||||
/// 각 Mount 에 대한 검사는 주기적으로 Thred 에 의해 동작된다.
|
||||
class CProperStorage
|
||||
{
|
||||
public:
|
||||
/// @brief 생성자
|
||||
/// @param [in] strStorageRoot 전체 Mount 된 경로 정보 중 사용할 최상위 경로 정보. ( /stg , /user2/dav_storage 등 )
|
||||
/// @param [in] nCheckInterval 각 Mount 정보에 대한 상태 검사 주기, 초(sec) 단위
|
||||
CProperStorage( const std::string & strStorageRoot, const int nCheckInterval );
|
||||
|
||||
~CProperStorage();
|
||||
|
||||
|
||||
/// @brief Thread 기동 함수
|
||||
/// @param none
|
||||
/// @return 성공시 true, 그 외 false
|
||||
bool Start();
|
||||
|
||||
|
||||
/// @brief 생성자에 전달된 최상위 경로 정보를 바탕으로 시스템에 Mount 된 정보를 검사하여...
|
||||
/// Content Write 에 가장 적절한 mount 경로정보를 전달한다.
|
||||
/// @param [out] strProperPath Content Write 에 가장 적절한 Mount 경로 ( /stg/node0, /stg/node1, /user2/dav_storage 등 )
|
||||
/// @return 성공시 true, 그 외 false
|
||||
bool GetProperPath( std::string& strProperPath );
|
||||
bool GetProperPath( std::string& strProperPath, unsigned long long & nAvailableSzie );
|
||||
|
||||
/// Content Write 에 가장 적절한 mount 경로정보의 Free Size 정보. ( Byte 단위)
|
||||
// - GetProperPath() 함수를 통해 경로 정보 수신 후 본 함수를 호출할 경우...
|
||||
// - Thread 에 의해 경로 정보 및 Free 용량 정보가 변경되어 다른 경로에 대한 Free 용량 정보가 전달될 수 있으니... 사용시 주의한다.
|
||||
void GetAvailableSize( unsigned long long & nAvailableSzie );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// 생성자를 통해 전달받은 최상위 경로 정보 저장.
|
||||
std::string m_strStorageRoot;
|
||||
// Mount 정보에 대한 검사 주기 (생성자를 통해 전달받음, sec 단위 )
|
||||
int m_nCheckInterval;
|
||||
|
||||
|
||||
// Thread 에서 검사된 최적의 Content Write 경로 정보 저장 변수. ( 주기적으로 변경됨 )
|
||||
std::string m_strProperPath;
|
||||
// 추출된 m_strProperPath 경로의 Free Size ( Byte 단위 )
|
||||
unsigned long long m_nAvailableSize;
|
||||
|
||||
|
||||
// 검사 결과 저장 관련 m_strProperPath, m_nAvailableSize 에 대해 동시 get, set 처리 관련 Mutext lock
|
||||
pthread_mutex_t m_mutexLock;
|
||||
|
||||
// systme 에서 추출된 mount 상태 정보를 저장하기 위한 vector 변수
|
||||
std::vector<struct systme_mount_info> m_vecMount;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// 쓰레드 핸들
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// 쓰레드 함수..
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 경로 최적 Mount 경로 정보 추출을 담당할 함수.
|
||||
void Execute( void );
|
||||
|
||||
// FHS 장비의 Mount 정보를 추출하여... 멤버 변수인 m_vecMount 에 저장한다.
|
||||
// return true : Mount 정보 정상 추출시...
|
||||
// false : Mount 정보 추출 실패 또는 추출된 Mount 정보가 없는 경우...
|
||||
bool GetSystmeMountInfo();
|
||||
|
||||
|
||||
// 전달받은 경로로부터.. 상위 Path 정보를 추출하여 전달한다.
|
||||
// 오류 발생시 [out] 값인 strUpperPath 가 empty 된다.
|
||||
void GetUpperPath( const std::string & strSourcePath, std::string & strUpperPath );
|
||||
|
||||
// Content Write 관련 최적 Mount 경로 및 사용 가능 Free Size 를 설정한다.
|
||||
void SetProperPath( std::string& strProperPath, unsigned long long & nAvailableSzie );
|
||||
|
||||
// Content Write 관련 최적 Mount 경로 및 사용 가능 Free Size 추출 실패시..
|
||||
void SetProperPathError();
|
||||
|
||||
// 인자로 전달받은 Mount 에 대해 disable flag 가 등록되어 있는지 검사
|
||||
bool CheckMountDisable( const std::string & strMountName);
|
||||
|
||||
|
||||
// vector 정렬 함수.
|
||||
//bool SortMountAvailableSize( struct systme_mount_info & fisrt, struct systme_mount_info & second);
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif //__PROPER_STORAGE_H__
|
||||
@@ -0,0 +1,85 @@
|
||||
/***************************************************************************
|
||||
ftsd ( File TranSfer Daemon ) Header ( Protocol.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/09
|
||||
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 __PROTOCOL_H__
|
||||
#define __PROTOCOL_H__
|
||||
|
||||
|
||||
struct FileTransferPacketHeader {
|
||||
|
||||
char stx; // Packet 유효성 관리 코드
|
||||
char type; // Request or Response 여부 ( 0x00: Request, 0x01: Response )
|
||||
char command[4]; // Command Code ( 4 Byte) : 0th control-ftsd, 1th ftsd-ftsd 사용.
|
||||
char result[4]; // Result Code ( 4 Byte )
|
||||
unsigned int data_length; // Packet Data 부분의 길이값 ( Network Byte Order 사용)
|
||||
char extend_code[2]; // 확장 및 Padding bits ( 2 Byte )
|
||||
};
|
||||
|
||||
|
||||
// Communication Packet Header stx 코드
|
||||
#define HEADER_STX_CODE 0x02
|
||||
|
||||
// Communication Packet Header type 구분코드
|
||||
#define HEADER_TYPE_REQUEST 0x00
|
||||
#define HEADER_TYPE_RESPONSE 0x01
|
||||
|
||||
|
||||
// Communication Packet command 공통.
|
||||
#define COMMON_ALIVE_CHECK 0x7F
|
||||
|
||||
|
||||
// Communication Packet command
|
||||
// control<->transfer 통신 (Command 첫번째 Byte 만 사용)
|
||||
//
|
||||
#define NOT_CONTROL_COMMAND 0x00 // Control 이 전송한 요청이 아닌 경우.
|
||||
#define CONTROL_FILE_REPLICATION 0x01 // Content Replication 처리 요청
|
||||
#define CONTROL_FILE_CACHE 0x02 // Content Cache 폴더로 복제 처리 요청
|
||||
|
||||
#define CONTROL_FILE_CHECK 0x03 // Content 에 대한 존재 여부, Size 확인 및 Hash 추출 요청
|
||||
#define CONTROL_FILE_UNLINK 0x04 // Content 에 대한 unlink 처리 요청
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460)
|
||||
#define CONTROL_FILE_CHECK_TOTO 0x05 // 토토디스크 고객사 지원을 위한 MD5 Hash 추출 기능 (2016-05-03 추가)
|
||||
|
||||
// NEW 2021-04-01 huibong content stat(size, atime, mtime, ctime) 정보 추출 기능 추가 (#33516)
|
||||
#define CONTROL_FILE_STAT 0x06
|
||||
|
||||
|
||||
// Communication Packet command
|
||||
// transfer<->transfer 간의 내부 통신 (Command 두번째 Byte 만 사용)
|
||||
//
|
||||
#define TRANSFER_LOCAL_IP 0x01 // 내부망 IP 요청 및 결과 처리시.
|
||||
#define TRANSFER_FILE_REPLICATION 0x02 // 파일 복제 처리시
|
||||
#define TRANSFER_FILE_CACHE 0x03 // 파일 Cache 처리시
|
||||
#define TRANSFER_FILE_HASH 0x04 // 파일 Hash Check
|
||||
#define TRANSFER_FILE_START 0x05 // 파일 전송 시작시 Header
|
||||
#define TRANSFER_FILE_END 0x06 // 파일 전송 종료시 전달. 수신 받은 파일 정보 요청 목적
|
||||
|
||||
|
||||
// Communication Packet Result
|
||||
// => type이 Response 인 경우에만 세팅됨.( 첫번째 Byte 만 사용시 )
|
||||
#define HEADER_RESULT_SUCCESS 0x00
|
||||
#define HEADER_RESULT_ERROR 0x01
|
||||
|
||||
|
||||
// 기타 정보
|
||||
#define LENGTH_FIELD_SIZE 4 // 가변데이터 형식 사용시 Length 필드의 메모리 크기 (unsigned int)
|
||||
|
||||
|
||||
// unlink 요청시 Mode 설정 정보
|
||||
#define UNLINK_MODE_NORMAL 0 // 해당 Content 가 존재하고 size 값이 정확한 경우에만 삭제, 나머지는 오류
|
||||
#define UNLINK_MODE_FORCE 1 // 해당 Content 가 존재하지 않거나.. Size 값이 틀려도 강제로 삭제 처리.. 오류로 처리되는 상황은 통신, 시스템 오류 발생시.
|
||||
|
||||
|
||||
#endif /* __PROTOCOL_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,243 @@
|
||||
/***************************************************************************
|
||||
ftsd ( File TranSfer Daemon ) Header ( SocketControl.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/09
|
||||
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 __SOCKET_CONTROL_H__
|
||||
#define __SOCKET_CONTROL_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "Protocol.h"
|
||||
#include "BaseSocket.h"
|
||||
|
||||
///< BYTE 타입 정의
|
||||
#ifndef _BYTE_DEFINED
|
||||
#define _BYTE_DEFINED
|
||||
typedef unsigned char BYTE;
|
||||
#endif // _BYTE_DEFINED
|
||||
|
||||
|
||||
#define DEFAULT_SOCKET_TEMP_BUFFER_SIZE 1024 // SocketControl 에서 사용할 임시버퍼 크기.
|
||||
#define FILE_DATA_BUFFER_SIZE 16384 // 16 Kbyte, 파일 송수신시 사용할 임시 버퍼의 크기.
|
||||
|
||||
class CSocketControl : public CBaseSocket
|
||||
{
|
||||
private:
|
||||
|
||||
/// @brief Packet Header 변수
|
||||
struct FileTransferPacketHeader m_packetHeader;
|
||||
|
||||
/// @brief m_packetHeader 구조체의 크기를 저장하기 위한 상수
|
||||
const int m_nPacketHeaderLen;
|
||||
|
||||
/// @brief Packet Header 에 저장된 Data 부분의 길이 정보값.
|
||||
unsigned int m_nPacketDataLen;
|
||||
|
||||
/// @brief Packet Data 부분의 수신처리시 임시로 사용할 버퍼.
|
||||
BYTE m_tempBuffer[DEFAULT_SOCKET_TEMP_BUFFER_SIZE];
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// @brief 생성자.
|
||||
/// @param socket [in] 처리할 socket descriptor
|
||||
|
||||
CSocketControl();
|
||||
CSocketControl( const int & socket );
|
||||
|
||||
/// @brief 소멸자.
|
||||
~CSocketControl();
|
||||
|
||||
|
||||
/// @brief 전달받은 Target 으로 Socket 접속을 수행
|
||||
/// @param szTarget [in] 접속 대상 Host name 또는 IP
|
||||
/// @param nPort [in] 접속 Port
|
||||
/// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
|
||||
bool ConnectTarget( const std::string& szTarget, int nPort );
|
||||
|
||||
/// @brief Packet 의 Header 정보를 수신하여 각 정보를 전달받은 참조변수로 반환.
|
||||
/// @param bRequest [out] Request 요청인지 여부
|
||||
/// @param bFromControl [out] Control 로 부터 전달된 Command 인지 여부
|
||||
/// @param command [out] Command 정보
|
||||
/// @return 1 : Data 수신 성공시
|
||||
/// 0 : Timeout 발생시
|
||||
/// -1 : 접속 종료시
|
||||
int GetHeaderInfo( bool& bRequest, bool& bFromControl, char& command );
|
||||
|
||||
/// @brief Packet 의 Header 정보 중 현재 전달받은 Header 의 Result 값을 반환
|
||||
/// @param result [out] result 4 Byte 부분의 값을 저장하기 위한 참조변수
|
||||
/// @return result 4 Byte 부분 중 첫번째 Byte 값을 반환.
|
||||
char GetHeaderResult(void );
|
||||
void GetHeaderResult( std::vector<char>& result );
|
||||
|
||||
/// @brief Packet Header 정보를 Log 파일에 Logging 처리
|
||||
void PrintHeaderToLog(void);
|
||||
|
||||
/// @brief Transfer Daemon 으로 Local NIC IP 정보 전송
|
||||
/// @param bResult [in] szValue 에 저장된 Local IP 정보의 유효여부
|
||||
/// @param szValue [in] Local NIC IP 정보
|
||||
/// @return 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendLocalIpInfo( bool bResult, const std::string& szValue );
|
||||
|
||||
/// @brief Socket 으로 연결된 대상의 Local NIC IP 정보를 요청한 후 해당 정보 수신.
|
||||
bool GetLocalIpInfo( std::string& szValue );
|
||||
|
||||
|
||||
|
||||
/// @brief 연결된 socket 으로 파일 수신 요청 패킷을 전송한다.
|
||||
bool SendFileReceiveRequest( std::string& szFileName, unsigned long long nFileSize, char commandCode );
|
||||
|
||||
/// @brief File 수신 요청에 대한 응답 패킷을 수신처리하기 위한 함수( TRANSFER_FILE_REPLICATION, TRANSFER_FILE_CACHE 요청에 대한 response 처리).
|
||||
/// 본 패킷이 정상인 경우 이 패킷을 수신한 Daemon 은 파일 전송 시작 패킷 및 File의 전송을 바로 시작한다.
|
||||
bool GetFileReceiveResponse( char commandCode, bool& bSuccess, std::string& szErrorString );
|
||||
|
||||
/// @brief File 송신 시작(TRANSFER_FILE_START::REQUEST) 요청 패킷.
|
||||
bool SendFileStartRequest(void);
|
||||
|
||||
/// @brief File 송신 완료후 해당 파일 정보 요청을 위한 (TRANSFER_FILE_END::REQUEST) 요청 패킷.
|
||||
bool SendFileEndRequest(void);
|
||||
|
||||
/// @brief 파일 수신 완료에 따른 응답패킷을 파싱처리.
|
||||
bool GetFileEndResult( bool& bSuccess, std::string& szFileFullName, unsigned long long& nFileSize, std::string& szErrorString );
|
||||
|
||||
|
||||
/// @brief 파일의 수신 요청을 받은 경우 해당 업무를 수행하기 위한 함수
|
||||
///< 본 함수는 파일의 수신, 수신 관련 통신 작업을 내부적으로 진행 처리함.
|
||||
bool ReceiveFile( const std::string& szStorageRoot, unsigned long long nMaxStorageAvailableSize, char commandCode );
|
||||
|
||||
|
||||
/// @brief 특정 파일에 대한 Hash 검사 요청을 연결된 Socket 을 통해 전송한다.
|
||||
bool SendHashCheckRequest(const std::string& szFileName, unsigned long long nFileSize
|
||||
, unsigned int nHashLevel, const std::string& szSourceHash );
|
||||
/// @brief 특정 파일에 대한 Hash 검사 요청을 받은 경우 이를 처리한다.
|
||||
bool CheckFileHash(void);
|
||||
|
||||
/// @brief 특정 파일에 대한 Hash 검사 결과를 연결된 Socket 으로 전송한다.
|
||||
bool SendHashCheckResponse( bool bSuccess, std::string& szErrorString );
|
||||
|
||||
/// @brief Hash 검사 요청에 대한 결과값을 수신한다.
|
||||
bool GetHashCheckResult( bool& bSuccess, std::string& szErrorString );
|
||||
|
||||
/// @brief Alive Check 를 위한 패킷 전송 => 해당 패킷의 응답은 필요없으므로 수신시 무시할 것.
|
||||
bool SendAliveCheck(void);
|
||||
|
||||
|
||||
/// @brief Control 로 부터 CONTROL_FILE_REPLICATION 또는 CONTROL_FILE_CACHE Request 요청을 받은 경우 \n
|
||||
///< Packet Data 부분의 상세 정보를 가져온다.
|
||||
bool GetFileRequestInfo( std::string& szFileName, unsigned long long& nFileSize, std::string& szTargetTranId,
|
||||
int& nFileHashCheckLevel, bool& bUseInternalIp, std::vector< std::string >& vecTargetFhs);
|
||||
|
||||
/// @brief Control 로 부터 COCONTROL_FILE_CHECK Request 요청을 받은 경우 Packet Data의 상세 정보를 가져온다.
|
||||
bool GetRequestCheckFromControl( std::string& szFileName, bool& bHashCheck, unsigned long long& nHashCheckSize );
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460)
|
||||
/// @brief Control 로 부터 CONTROL_FILE_CHECK_TOTO Request 요청을 받은 경우 Packet Data의 상세 정보를 가져온다.
|
||||
bool GetRequestCheckTOTOFromControl( std::string& szFileName );
|
||||
|
||||
|
||||
/// @brief Control 로 부터 COCONTROL_FILE_UNLINK Request 요청을 받은 경우 Packet Data의 상세 정보를 가져온다.
|
||||
bool GetRequestUnlinkFromControl( std::string& szFileName, unsigned long long& nFileSize, unsigned int& nUnlinkMode );
|
||||
|
||||
// NEW 2021-04-01 huibong content stat(size, atime, mtime, ctime) 정보 추출 기능 추가 (#33516)
|
||||
/// @brief Control 로 부터 CONTROL_FILE_STAT Request 요청을 받은 경우 Packet Data의 상세 정보를 가져온다.
|
||||
bool GetRequestStatFromControl( std::string & szFileName );
|
||||
|
||||
|
||||
|
||||
/// @brief Control 로 요청에 대한 오류 결과 메시지를 전송한다.
|
||||
bool SendErrorMessageToControl( const std::string& szErrorMessage, char commandCode );
|
||||
|
||||
/// @brief Control 로 파일 복제 요청에 대한 성공 처리 결과 메시지를 전송한다.
|
||||
bool SendFileReplicationResult( char commandCode
|
||||
, std::map< std::string, std::string>& mapSuccessList, std::map< std::string, std::string>& mapFailList );
|
||||
|
||||
/// @brief Control 로 Check 요청에 대한 성공 처리 결과를 전달한다.
|
||||
bool SendCheckResultToControl( char commandCode, const unsigned long long& nFileSize, const std::string& szHashValue );
|
||||
|
||||
/// @brief Control 로 Unlink 요청에 대한 성공 처리 결과를 전달한다... Data 부분은 존재하지 않음.
|
||||
bool SendUnlinkResultToControl( char commandCode );
|
||||
|
||||
|
||||
// NEW 2016-05-11 huibong 토토디스크 지원용 기능 추가 (#27460, #27533)
|
||||
/// @brief 고객사 토토디스크 지원을 위한 기능
|
||||
/// Control 로 부터 전달받은 Content 에 대해 Size 정보 및 MD5 Hash, Checksum 정보 등을 추출하여 전달한다.
|
||||
bool SendTOTOCheckResultToControl( char commandCode, const unsigned long long& nFileSize
|
||||
, const std::string& szResultTotoMd5, const std::string& szResultChecksum, const std::string& szResultDnaCheckKey );
|
||||
|
||||
// NEW 2021-04-01 huibong content stat(size, atime, mtime, ctime) 정보 추출 기능 추가 (#33516)
|
||||
/// @brief Control 로 Stat 요청에 대한 성공 처리 결과를 전달한다.
|
||||
bool SendStatResultToControl( char commandCode, const long & nFileSize, const long & nAccessTime, const long & nModifyTime, const long & nChangeTime );
|
||||
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Packet Header 부분의 수신 처리를 위한 함수. Alive Check 요청 패킷은 자동으로 무시처리함.
|
||||
/// @param timeout [in] 대기시간.
|
||||
/// @retrun ( 해당 Define 은 SocketControl.cpp 참고 )
|
||||
/// 1 : PACKET_HEADER_RECEIVE_SUCCESS : 성공시
|
||||
/// 0 : PACKET_HEADER_RECEIVE_TIMEOUT : Timeout 발생시
|
||||
/// -1 : PACKET_HEADER_RECEIVE_SOCK_CLOSED : socket 종료된 경우
|
||||
/// -2 : PACKET_HEADER_RECEIVE_INVALID_PROTOCOL : 잘못된 Packet 형식인 경우
|
||||
/// -3 : PACKET_HEADER_RECEIVE_READ_ERROR : 기타 오류
|
||||
int GetPacketHeader(int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT);
|
||||
|
||||
/// @brief socket 에서 지정된 크기만큼의 데이터를 읽어 출력변수에 저장처리.
|
||||
/// @param size [in] read 할 데이터 크기
|
||||
/// @param value [out] 읽은 데이터를 저장할 string 변수
|
||||
/// @return On success return true, otherwise return false.
|
||||
bool GetPacketData( unsigned int& size, std::string& value );
|
||||
|
||||
/// @brief socket 에서 지정된 크기만큼의 데이터를 읽어 내부 임시버퍼인 m_tempBuffer 에 저장처리.
|
||||
/// @param size [in] read 할 데이터 크기
|
||||
/// @return On success return true, otherwise return false.
|
||||
bool GetPacketData( unsigned int size );
|
||||
|
||||
/// @brief pValue 에 저장된 데이터를 unsigned int 형으로 변환처리 및 Endian 변환
|
||||
unsigned int GetDataToUInt( BYTE * pValue, bool bConvertEndian = true );
|
||||
/// @brief pValue 에 저장된 데이터를 unsigned long long (64Byte) 형으로 변환처리.
|
||||
unsigned long long GetDataToUInt64( BYTE * pValue );
|
||||
|
||||
|
||||
/// @brief 전달받은 Directory 정보를 확인하여 존재하지 않을 경우 생성처리.
|
||||
bool MakeDirectory( const std::string& szFullPath );
|
||||
|
||||
/// @brief Socket 으로 부터 수신받은 데이터를 파일에 Write 처리한다.
|
||||
///< 만약 File Write 처리시 오류가 발생하는 경우 Socket Data 의 정상적인 처리를 위해
|
||||
///< Socket 데이터 수신만 처리하고 오류를 반환한다.
|
||||
/// @returnFile Write 가 오류 없이 정상적으로 처리된 경우 true, 오류발생시 false 및 오류메시지 반환
|
||||
bool ReceiveToFile( FILE * pFile, unsigned long long nFileSize, std::string& errorMessage );
|
||||
|
||||
// Socket 으로 부터 수신받은 데이터를 읽어서.. 버린다.
|
||||
// - 기존 물리 파일이 존재하는 경우.. overwrite 처리를 않기로 해서...
|
||||
// - socket 으로 부터 수신된 data 는 버린다.
|
||||
// return 오류 없이 정상적으로 처리된 경우 true, 오류발생시 false 및 오류메시지 반환
|
||||
bool ReceiveToDummy( unsigned long long nFileSize, std::string& errorMessage );
|
||||
|
||||
|
||||
/// @brief File 수신 요청에 대한 응답 패킷 전송( TRANSFER_FILE_REPLICATION, TRANSFER_FILE_CACHE 요청에 대한 response 패킷임.
|
||||
/// 해당 패킷이 정상인 경우 이 패킷을 수신한 Daemon 은 파일 전송 시작 패킷 및 File의 전송을 바로 시작한다.
|
||||
bool SendFileReceiveResponse( bool bSuccess, std::string& szErrorString, char commandCode );
|
||||
|
||||
/// @brief File 송신 완료(TRANSFER_FILE_END) 패킷에 대한 응답 패킷 전송
|
||||
bool SendFileEndResponse( bool bSuccess, std::string& szFileFullName, unsigned long long nFileSize, std::string& szErrorString );
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __SOCKET_CONTROL_H__ */
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
#include "TOTOChecksum.h"
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
typedef unsigned long long UINT64;
|
||||
|
||||
|
||||
// 토토디스크 고객사용 CRC Table.
|
||||
long g_arrayTotoCrc[256] = {
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
|
||||
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
|
||||
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
|
||||
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
|
||||
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
|
||||
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
|
||||
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
|
||||
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
|
||||
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
|
||||
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
|
||||
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
|
||||
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
|
||||
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
|
||||
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
|
||||
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
|
||||
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
|
||||
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
|
||||
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
|
||||
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
|
||||
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
|
||||
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
|
||||
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 생성자
|
||||
CTotoChecksum::CTotoChecksum()
|
||||
{
|
||||
// 오류메시지 객체 초기화.
|
||||
m_szErrorMessage.clear();
|
||||
}
|
||||
|
||||
|
||||
// 소멸자
|
||||
CTotoChecksum::~CTotoChecksum()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int CTotoChecksum::calcCRC32( unsigned char b, unsigned int iCrc32 )
|
||||
{
|
||||
//return (int)((dwCrc32 >>> 8) ^ CRC_TABLE[(dwCrc32 ^ b) & 0xFF]);
|
||||
|
||||
// JAVA >>> 연산은 C 의 >> 와 동일하지만.. 빈 공간을 무조건 0 채운다.
|
||||
// 따라서.. 이를 지원하기 위해.. iCrc32 인자값을 unsigned int 형으로 변경 처리한다.
|
||||
|
||||
//_LOG( LDBG, "TOTO calcCRC32 byte[%c] crc[%d] [%d] [%d]", b, iCrc32, (iCrc32 >> 8), (iCrc32 ^ b) );
|
||||
|
||||
return (int)((iCrc32 >> 8) ^ g_arrayTotoCrc[(iCrc32 ^ b) & 0xFF]);
|
||||
}
|
||||
|
||||
|
||||
/// @brief 토토디스크 고객사 지원용 파일 Checksum 정보 추출 함수
|
||||
/// @param szFileFullPath [in] 파일의 절대경로 값.
|
||||
/// @param nFileTotalSize [in] 파일 크기 값.
|
||||
/// @param szResult [out] Checksum 추출 결과값을 반환하기 위한 변수.
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool CTotoChecksum::GetChecksum( const std::string &szFileFullPath, const unsigned long long nFileTotalSize, std::string& szResult )
|
||||
{
|
||||
int fd;
|
||||
unsigned char szBuffer[4096];
|
||||
|
||||
int nLen;
|
||||
UINT64 nFileSkipCount;
|
||||
int i;
|
||||
|
||||
long nResultValue = 0;
|
||||
int iCrc32 = 0xffffffff;
|
||||
|
||||
memset( szBuffer, 0x00, sizeof( szBuffer ) );
|
||||
|
||||
// 저장변수 초기화.
|
||||
szResult.clear();
|
||||
|
||||
// 파일 open
|
||||
if( (fd = open( szFileFullPath.c_str(), O_RDONLY )) == -1 )
|
||||
{
|
||||
m_szErrorMessage = "File open failed. [" + szFileFullPath + "]";
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 파일을 10 개 지점으로 분할하여 지점별 4096 Byte 씩 추출하여 CRC 계산 처리 수행.
|
||||
nFileSkipCount = (nFileTotalSize > 0 ? nFileTotalSize / 10 : 0);
|
||||
while( (nLen = read( fd, szBuffer, 4096 )) )
|
||||
{
|
||||
for( i = 0; i < nLen; i++ )
|
||||
{
|
||||
iCrc32 = calcCRC32( szBuffer[i], iCrc32 );
|
||||
}
|
||||
|
||||
lseek( fd, (int)nFileSkipCount, SEEK_CUR );
|
||||
}
|
||||
|
||||
//_LOG( LDBG, "TOTO Checksum #1 [%d]", iCrc32 );
|
||||
|
||||
// 파일 크기 정보를 이용하여 추가로 CRC 계산 처리.
|
||||
memset( szBuffer, 0x00, sizeof( szBuffer ) );
|
||||
sprintf( (char *)szBuffer, "%012llu", nFileTotalSize );
|
||||
nLen = strlen( (char *)szBuffer );
|
||||
|
||||
for( i = 0; i < nLen; i++ )
|
||||
{
|
||||
iCrc32 = calcCRC32( szBuffer[i], iCrc32 );
|
||||
//_LOG( LDBG, "TOTO Checksum #2 byte[%c] crc[%d]", szBuffer[i], iCrc32 );
|
||||
}
|
||||
|
||||
//_LOG( LDBG, "TOTO Checksum #3 [%d][%s]", iCrc32, szBuffer );
|
||||
|
||||
// 최종 결과에 대한 마지막 변환 계산.
|
||||
nResultValue = ~iCrc32 & 0xFFFFFFFFL;
|
||||
//_LOG( LDBG, "TOTO Checksum #4 [%ld]", nResultValue );
|
||||
|
||||
// 반환값 처리를 위한 정수를 문자열로 변환 처리.
|
||||
memset( szBuffer, 0x00, sizeof( szBuffer ) );
|
||||
sprintf( (char*)szBuffer, "%ld", nResultValue );
|
||||
szResult.append( (char *)szBuffer );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/***************************************************************************
|
||||
Totodisk Checksum Class (#27460, #27533)
|
||||
-----------------------------------------
|
||||
begin : 2016/05/11
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : huibong
|
||||
email : storage.sd@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 __TOTO_CHECKSUM_H__
|
||||
#define __TOTO_CHECKSUM_H__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
|
||||
/// @brief CTotoChecksum class
|
||||
/// 토토디스크 고객사 지원을 위한 클래스로서
|
||||
/// 해당 고객사에서 제공한 파일의 Checksum 추출 기능 (java 코드) 을
|
||||
/// 구현하기 위한 클래스임.
|
||||
///
|
||||
/// 본 클래스의 코드는
|
||||
/// 토토디스크에서 제공한 Java 소스를 이용하여 최대한 유사하게 구현하였으며...
|
||||
/// 비효율적인 코드가 존재하더라고.. 고객사 코드를 최대한 그대로 변환하려 한 것이므로....
|
||||
/// 함부로 최적화 하면 절대 안된다....(허락 없이 수정시 옥상에서 던져버릴 것임)
|
||||
///
|
||||
/// 에러 상황은 GetErrorMessage() 함수를 통해 어떤이유로 에러가 발생했는지
|
||||
/// 확인 할 수 있다.
|
||||
///
|
||||
class CTotoChecksum
|
||||
{
|
||||
public :
|
||||
|
||||
// 생성자
|
||||
CTotoChecksum();
|
||||
|
||||
// 소멸자
|
||||
~CTotoChecksum();
|
||||
|
||||
|
||||
/// @brief 토토디스크 고객사 지원용 파일 Checksum 정보 추출 함수
|
||||
/// @param szFileFullPath [in] 파일의 절대경로 값.
|
||||
/// @param nFileTotalSize [in] 파일 크기 값.
|
||||
/// @param szResult [out] Checksum 추출 결과값을 반환하기 위한 변수.
|
||||
/// @return 성공은 return true, 그 외에는 return false
|
||||
bool GetChecksum( const std::string &szFileFullPath, const unsigned long long nFileTotalSize, std::string& szResult );
|
||||
|
||||
|
||||
/// @brief 각 함수 호출시 마지막에 난 에러메세지를 얻을 수 있다.
|
||||
/// @return 마지막에 발생한 에러메세지.
|
||||
std::string GetErrorMessage( void ) { return m_szErrorMessage; }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// 토토에서 제공한 CRC 연산 함수.
|
||||
int calcCRC32( unsigned char b, unsigned int iCrc32 );
|
||||
|
||||
|
||||
private :
|
||||
|
||||
std::string m_szErrorMessage;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif //__TOTO_CHECKSUM_H__
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
#include "TargetFhsInfo.h"
|
||||
|
||||
CTargetFhsInfo::CTargetFhsInfo( std::string szHostName )
|
||||
: m_szHostName( szHostName )
|
||||
, m_szIp( szHostName )
|
||||
, m_bTransferSucess( true )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CTargetFhsInfo::~CTargetFhsInfo()
|
||||
{
|
||||
m_socketControl.Close();
|
||||
}
|
||||
|
||||
/// @brief 대상 FHS로 접속을 시도한다.
|
||||
bool CTargetFhsInfo::MakeConnection( bool bUseInternalIp, CIPCache * pIpCache )
|
||||
{
|
||||
// 내부망을 사용하지 않는 경우
|
||||
if( bUseInternalIp == false )
|
||||
{
|
||||
// m_szIp 저장된 정보를 이용하여 접속 시도처리.
|
||||
if( m_socketControl.ConnectTarget( m_szHostName, SVC_LISTEN_PORT ) == false )
|
||||
{
|
||||
m_bTransferSucess = false;
|
||||
//m_szErrorMessage = "Can't connect to target FHS [" + m_szHostName + "]";
|
||||
m_szErrorMessage = "connection fail by hostname";
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bTransferSucess = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 내부망을 사용하는 경우
|
||||
// 1. Cache 검색 처리
|
||||
// 2. Cache 에 존재하지 않는 경우
|
||||
// - 기존 외부망 정보를 이용하여 접속 시도
|
||||
// - 내부망 IP 정보 요청 및 Cache 처리
|
||||
// - 내부망 IP 로 접속 시도
|
||||
// - 내부망 IP 접속 실패시 다시 외부망으로 연결 처리
|
||||
// 3. Cache 에 존재하면 해당 정보를 이용하여 접속 시도
|
||||
// - 내부망 접속 실패시 외부망으로 연결 처리.
|
||||
|
||||
std::string szTempIp;
|
||||
|
||||
// Cache 에 대상 FHS 정보가 존재하는 경우
|
||||
if( pIpCache->GetPrivateIP( m_szHostName, szTempIp ) == true )
|
||||
{
|
||||
// 접속 시도해서 성공하면 OK
|
||||
if( m_socketControl.ConnectTarget( szTempIp, SVC_LISTEN_PORT ) == true )
|
||||
{
|
||||
// 멤버변수의 m_szIp 정보를 변경 처리
|
||||
m_szIp = szTempIp;
|
||||
m_bTransferSucess = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 그외의 경우
|
||||
// - Cache 에 저장된 정보로 접속 시도 실패시
|
||||
// - Cache 에 저장된 정보가 없는 경우.
|
||||
|
||||
// 일단 외부망으로 접속 시도
|
||||
if( m_socketControl.ConnectTarget( m_szHostName, SVC_LISTEN_PORT ) == false )
|
||||
{
|
||||
// 외부망 접속 실패시 => 더이상 방법이 없으므로 실패 처리
|
||||
m_socketControl.Close();
|
||||
|
||||
m_bTransferSucess = false;
|
||||
//m_szErrorMessage = "Can't connect to target FHS[" + m_szHostName + "]";
|
||||
m_szErrorMessage = "connection fail by hostname";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 외부망 접속이 성공한 경우.
|
||||
// - 내부망 IP 정보 요청 처리.
|
||||
if( m_socketControl.GetLocalIpInfo( szTempIp ) == false )
|
||||
{
|
||||
// 내부망 IP 요청 처리 실패시.
|
||||
// 현재의 접속된 외부망이 유효한지 확인하여
|
||||
// 유효한 경우 외부망 접속을 그냥 사용
|
||||
// 유효하지 않은 경우 에러를 반환.
|
||||
|
||||
if( m_socketControl.GetConnectionStatus() == false )
|
||||
{
|
||||
m_bTransferSucess = false;
|
||||
//m_szErrorMessage = "Can't connect to target FHS[" + m_szHostName + "]";
|
||||
m_szErrorMessage = "connection fail by hostname";
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bTransferSucess = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 외부망을 통해 내부망 정보 수신을 성공한 경우.
|
||||
// 내부망 정보로 접속 시도.
|
||||
m_socketControl.Close();
|
||||
if( m_socketControl.ConnectTarget( szTempIp, SVC_LISTEN_PORT ) == true )
|
||||
{
|
||||
// 내부망 접속이 성공한 경우.
|
||||
// 해당 정보를 Cache 및 멤버 변수에 설정.
|
||||
m_szIp = szTempIp;
|
||||
pIpCache->SetPrivateIP( m_szHostName, szTempIp);
|
||||
m_bTransferSucess = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 전달받은 내부망 정보로 접속이 실패한 경우
|
||||
// 다시 외부망으로 접속 시도.
|
||||
m_socketControl.Close();
|
||||
if( m_socketControl.ConnectTarget( m_szHostName, SVC_LISTEN_PORT ) == false )
|
||||
{
|
||||
// 외부망 접속 실패시 => 더이상 방법이 없으므로 실패 처리
|
||||
m_socketControl.Close();
|
||||
|
||||
m_bTransferSucess = false;
|
||||
m_szErrorMessage = "connection fail by hostname";
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 마지막 외부망으로 접속이 성공한 경우.
|
||||
m_szIp = m_szHostName;
|
||||
m_bTransferSucess = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void CTargetFhsInfo::PrintToConnectionInfo()
|
||||
{
|
||||
_LOG( LINF, "FHS Target host[%s] IP[%s] Connect[%s]"
|
||||
, m_szHostName.c_str(), m_szIp.c_str()
|
||||
, ( m_socketControl.GetConnectionStatus() == true ? "YES" : "NO"));
|
||||
}
|
||||
|
||||
/// @brief 대상 FHS 로 접속이 유효한지 여부를 판단하기 위한 함수.
|
||||
bool CTargetFhsInfo::IsAvailable()
|
||||
{
|
||||
if( m_socketControl.GetConnectionStatus() == true && m_bTransferSucess == true )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief 대상 FHS 로 파일 전송 시작을 위한 패킷 전송.
|
||||
bool CTargetFhsInfo::ReadyToFileTransfer( std::string& szFileFullName, unsigned long long nFileSize, std::string& szTargetTranId, char command )
|
||||
{
|
||||
// 전송할 준비가 안된 경우.
|
||||
if( m_bTransferSucess == false )
|
||||
return false;
|
||||
|
||||
// 전달받은 파일 정보에서 현재의 Mount 정보를 제거한다.
|
||||
// 방식은 뒤에서 / 문자 두개를 찾아서 처리
|
||||
size_t pos = szFileFullName.rfind( '/' );
|
||||
pos = szFileFullName.rfind('/', pos-1);
|
||||
std::string szFileName = szFileFullName.substr( pos+1 );
|
||||
|
||||
// 2010-05-07 ADD huibong
|
||||
// 인자로 전달받은 szTargetTranId 값의 존재 여부를 확인하여
|
||||
// 해당 값이 존재하는 경우 기존 Tran ID 정보를 전달받은 Tran ID 정보를 변환처리하여 전달한다. ( RC 간 전송처리시 사용)
|
||||
if( szTargetTranId.size() > 0 )
|
||||
{
|
||||
pos = szFileName.rfind( '/' );
|
||||
std::string szTempFileName = szFileName.substr ( pos+1 );
|
||||
|
||||
szFileName = szTargetTranId + "/" + szTempFileName;
|
||||
}
|
||||
|
||||
|
||||
// Socket Control로 파일 전송에 대한 요청 패킷 전송.
|
||||
if( m_socketControl.SendFileReceiveRequest( szFileName, nFileSize, command) == false )
|
||||
{
|
||||
// 요청 패킷 전달 실패시.
|
||||
m_bTransferSucess = false;
|
||||
m_szErrorMessage = "Can't send packet.[file transfer request][" + m_szHostName + "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 요청에 대한 응답 대기.
|
||||
if( m_socketControl.GetFileReceiveResponse( command, m_bTransferSucess, m_szErrorMessage ) == false )
|
||||
{
|
||||
// 응답패킷 수신 실패시
|
||||
m_bTransferSucess = false;
|
||||
m_szErrorMessage = "Can't receive packet.[file transfer response][" + m_szHostName + "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 현재 파일 전송이 가능한지 응답 결과를 보고 판단한다.
|
||||
if( m_bTransferSucess == false )
|
||||
return false;
|
||||
|
||||
// 파일 전송 시작 패킷 전송.
|
||||
if( m_socketControl.SendFileStartRequest() == false )
|
||||
{
|
||||
m_bTransferSucess = false;
|
||||
m_szErrorMessage = "Can't send packet.[file transfer start packet][" + m_szHostName + "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief 파일 Data 의 전송을 처리하기 위한 함수
|
||||
bool CTargetFhsInfo::SendBufferToFile(const void * buffer, size_t & size )
|
||||
{
|
||||
if( m_bTransferSucess == false )
|
||||
return false;
|
||||
|
||||
// 전달받은 패킷을 해당 socket 으로 전송 처리.
|
||||
int nWrite = m_socketControl.WriteN( buffer, size );
|
||||
if( nWrite != (int) size )
|
||||
{
|
||||
// 오류 발생시 : WriteN 함수 반환값 0: Socket Closed, -1: 오류 발생시
|
||||
m_bTransferSucess = false;
|
||||
m_szErrorMessage = "Can't send packet.[file data send fail.][" + m_szHostName + "]";
|
||||
|
||||
LOG( LERR, "File data write fail.[%d/%zu][%s]" , nWrite, size, m_szHostName.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief 파일 전송 완료후 전송 결과에 대한 응답패킷을 수신한다.
|
||||
bool CTargetFhsInfo::GetFileTransferResult()
|
||||
{
|
||||
if( m_bTransferSucess == false )
|
||||
{
|
||||
LOG( LERR, "[%s] TRANSFER_FILE_END process fail by flag" , m_szHostName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 파일 전송 완료 Packet 전송.
|
||||
if( m_socketControl.SendFileEndRequest() == false )
|
||||
{
|
||||
LOG( LERR, "Cannot send TRANSFER_FILE_END request [%s]", m_szHostName.c_str());
|
||||
|
||||
m_bTransferSucess = false;
|
||||
m_szErrorMessage = "Can't send TRANSFER_FILE_END Request [" + m_szHostName + "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 전송 완료에 대한 응답패킷 수신.
|
||||
if( m_socketControl.GetFileEndResult( m_bTransferSucess, m_szTransferResultFileName, m_nTransferResultFileSize, m_szErrorMessage) == false )
|
||||
{
|
||||
// 응답패킷 수신 실패시
|
||||
LOG( LERR, "Cannot receive TRANSFER_FILE_END response [%s]", m_szHostName.c_str());
|
||||
|
||||
m_bTransferSucess = false;
|
||||
m_szErrorMessage = "Can't receive TRANSFER_FILE_END response [" + m_szHostName + "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_bTransferSucess == false )
|
||||
{
|
||||
LOG( LDBG, "[%s] TRANSFER_FILE_END process COMPLETE TO FAIL[%s]", m_szHostName.c_str(), m_szErrorMessage.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LDBG, "[%s] TRANSFER_FILE_END process COMPLETE TO SUCCESS[%s]", m_szHostName.c_str(), m_szTransferResultFileName.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// @brief 파일 Hash 검사를 요청하고 이에 대한 결과를 반환받는다.
|
||||
bool CTargetFhsInfo::SendFileHashCheckRequest( int nHashLevel, unsigned long long nFileSize, std::string& szSourceHash)
|
||||
{
|
||||
if( m_bTransferSucess == false )
|
||||
{
|
||||
LOG( LERR, "[%s] TRANSFER_FILE_HASH process fail by flag", m_szHostName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 만약 전달받은 File Name 이 존재하지 않는 경우
|
||||
if( m_szTransferResultFileName.size() == 0 )
|
||||
{
|
||||
LOG( LERR, "[%s] TRANSFER_FILE_HASH fail [target file name not valid]", m_szHostName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hash 검사 요청 메시지 전송.
|
||||
if( m_socketControl.SendHashCheckRequest( m_szTransferResultFileName, nFileSize, nHashLevel, szSourceHash ) == false )
|
||||
{
|
||||
m_bTransferSucess = false;
|
||||
m_szErrorMessage = "Can't send packet.[file hash check start packet][" + m_szHostName + "]";
|
||||
|
||||
LOG( LERR, "[%s] TRANSFER_FILE_HASH request send fail [%s]", m_szHostName.c_str(), m_szTransferResultFileName.c_str());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LDBG, "[%s] TRANSFER_FILE_HASH request send success [%s][%d][%s]", m_szHostName.c_str(), m_szTransferResultFileName.c_str(), nHashLevel, szSourceHash.c_str() );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CTargetFhsInfo::GetFileHashCheckResult()
|
||||
{
|
||||
if( m_bTransferSucess == false )
|
||||
{
|
||||
LOG( LERR, "[%s] TRANSFER_FILE_HASH process fail by flag", m_szHostName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hash 검사 완료에 대한 응답 패킷 수신.
|
||||
if( m_socketControl.GetHashCheckResult( m_bTransferSucess, m_szErrorMessage) == false )
|
||||
{
|
||||
// 응답패킷 수신 실패시
|
||||
m_bTransferSucess = false;
|
||||
m_szErrorMessage = "Can't receive TRANSFER_FILE_HASH response [" + m_szHostName + "]";
|
||||
|
||||
LOG( LERR, "Cannot receive TRANSFER_FILE_HASH response [%s][%s]", m_szHostName.c_str(), m_szTransferResultFileName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CTargetFhsInfo::GetResult( std::string& szHostName, std::string& szFileFullName, std::string& szErrorMessage)
|
||||
{
|
||||
szHostName = m_szHostName;
|
||||
szFileFullName = m_szTransferResultFileName;
|
||||
szErrorMessage = m_szErrorMessage;
|
||||
|
||||
return m_bTransferSucess;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/***************************************************************************
|
||||
ftsd ( File TranSfer Daemon ) Header ( TargetFhsInfo.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/13
|
||||
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 __TARGET_FHS_INFO_H__
|
||||
#define __TARGET_FHS_INFO_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "SocketControl.h"
|
||||
#include "IPCache.h"
|
||||
|
||||
class CTargetFhsInfo
|
||||
{
|
||||
public:
|
||||
|
||||
CTargetFhsInfo( std::string szHostName );
|
||||
~CTargetFhsInfo();
|
||||
|
||||
/// @brief 대상 FHS로 접속을 시도한다.
|
||||
bool MakeConnection( bool bUseInternalIp, CIPCache * pIpCache );
|
||||
|
||||
/// @brief 대상 FHS 로 접속이 유효한지 여부를 판단하기 위한 함수.
|
||||
bool IsAvailable(void);
|
||||
|
||||
/// @brief 대상 FHS 의 접속 정보를 반환.
|
||||
std::string GetHostName(void) { return m_szHostName; }
|
||||
|
||||
/// @brief 오류발생시 마지막 오류 문자열을 반환.
|
||||
std::string GetErrorMessage(void) { return m_szErrorMessage; }
|
||||
|
||||
/// @brief Log 파일 상에 Connection 관련 정보를 표시.
|
||||
void PrintToConnectionInfo(void);
|
||||
|
||||
/// @brief 대상 FHS 로 파일 전송 시작을 위한 패킷 전송.
|
||||
bool ReadyToFileTransfer( std::string& szFileFullName, unsigned long long nFileSize, std::string& szTargetTranId, char command );
|
||||
|
||||
/// @brief 파일 전송 완료후 전송 결과에 대한 응답패킷을 수신한다.
|
||||
bool GetFileTransferResult(void);
|
||||
|
||||
/// @brief 파일 Data 의 전송을 처리하기 위한 함수
|
||||
bool SendBufferToFile(const void * buffer, size_t& size );
|
||||
|
||||
/// @brief 파일 전송 처리에 대한 결과 정보를 반환.
|
||||
bool GetResult( std::string& szHostName, std::string& szFileFullName, std::string& szErrorMessage);
|
||||
|
||||
/// @brief 파일 Hash 검사를 요청한다.
|
||||
bool SendFileHashCheckRequest( int nHashLevel, unsigned long long nFileSize, std::string& szSourceHash);
|
||||
|
||||
/// @brief 파일 Hash 결과 검사에 대한 결과 정보를 수신한다.
|
||||
bool GetFileHashCheckResult(void);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
CSocketControl m_socketControl; // Socket 및 Packet 생성 및 처리를 담당하기 위한 Socket Control Class
|
||||
|
||||
std::string m_szHostName; // Target FHS Host Name
|
||||
std::string m_szIp; // Target FHS 접속 정보 - 내부망을 사용하지 않는 경우 m_szHostName 과 동일
|
||||
|
||||
bool m_bTransferSucess;
|
||||
std::string m_szErrorMessage;
|
||||
std::string m_szTransferResultFileName;
|
||||
unsigned long long m_nTransferResultFileSize;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __TARGET_FHS_INFO_H__ */
|
||||
@@ -0,0 +1,999 @@
|
||||
#include "Worker.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include "TOTOChecksum.h" // NEW 2016-05-11 토토디스크 고객사 checksum 추출 지원 목적.
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
#define DEFAULT_ALIVE_CHECK_TIME 5 // sec
|
||||
|
||||
// 1. config 정보를 저장하기 위한 전역 객체 => Listener.cpp 에 정의
|
||||
extern struct st_config g_envConfig;
|
||||
|
||||
// 2. Listener Socket
|
||||
// => 시그널 처리시 문제로 인해 전역함수로 생성. initialize listener.cpp
|
||||
extern int g_listenSocket;
|
||||
|
||||
|
||||
/// @brief Worker Process 의 main 함수
|
||||
/// @param listenSocket [in] Listen Socket Descriptor
|
||||
int WorkerMain( const int listenSocket )
|
||||
{
|
||||
// Set Signal Handler
|
||||
SetSignalWorker();
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
setproctitle( "Worker [initialize process]");
|
||||
#endif
|
||||
|
||||
// Object initialize
|
||||
|
||||
// 1. Local NIC Cache Class 생성 및 초기화.
|
||||
CLocalIPCache localIpCache( g_envConfig.szInternalNicName);
|
||||
localIpCache.Start();
|
||||
|
||||
// 2. Target 내부 IP Cache Class 생성 및 초기화
|
||||
CIPCache ipCache;
|
||||
ipCache.SetTimeout( g_envConfig.nFhsInternalIpCacheTimeout );
|
||||
|
||||
// 3. Process Status 생성 및 초기화
|
||||
CProcessStatus processStatus;
|
||||
|
||||
// 4. Replication 처리시 내부 저장위치를 알려줄 Class 초기화.
|
||||
CProperStorage properStorage( g_envConfig.szStorageRoot, g_envConfig.nProperStorageRefresPeriod );
|
||||
if( properStorage.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "Worker[%d]: Proper Storage thread create failed. => Process Exit", getpid() );
|
||||
exit( EXIT_FAILURE ); // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// 접속 요청을 변수 생성 및 초기화.
|
||||
int clientSocket;
|
||||
struct sockaddr_in clientSockAddr;
|
||||
socklen_t clientSockLen = sizeof(clientSockAddr);
|
||||
|
||||
// Thread 관련 변수.
|
||||
pthread_t workerThreadId;
|
||||
struct WorkerThreadArg * pThreadArg = NULL;
|
||||
|
||||
// 접속 요청 대기 및 처리 루프
|
||||
#if defined(__FreeBSD__)
|
||||
setproctitle( "Worker [wait for connection]");
|
||||
#endif
|
||||
|
||||
while(1)
|
||||
{
|
||||
clientSocket = accept( listenSocket, (struct sockaddr *) &clientSockAddr, &clientSockLen );
|
||||
if( clientSocket == -1 )
|
||||
{
|
||||
// 오류발생시 해당 내역 로깅처리.
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Worker process: client accept failed. [%d][%s]", errorNum, strerror(errorNum) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// 정상적인 client 인 경우
|
||||
// 1. 변수 전달을 위한 객체 생성
|
||||
pThreadArg = ( struct WorkerThreadArg *)malloc( sizeof(struct WorkerThreadArg));
|
||||
if( pThreadArg == NULL)
|
||||
{
|
||||
int errorNum = errno;
|
||||
close( clientSocket );
|
||||
|
||||
// Error Logging
|
||||
// CHG 2012-02-01 huibong
|
||||
// inet_ntoa() 함수가 Thread-Safe 하지 않으므로 inet_ntop() 함수로 변경처리
|
||||
//LOG( LERR, "Worker Thread argument malloc failed.[%s]->[%d][%s]",inet_ntoa(clientSockAddr.sin_addr), errorNum, strerror( errorNum ));
|
||||
|
||||
char tempBuffer[INET_ADDRSTRLEN];
|
||||
memset(tempBuffer, 0x00, sizeof(tempBuffer));
|
||||
|
||||
if( inet_ntop( AF_INET, &(clientSockAddr.sin_addr), tempBuffer, sizeof(tempBuffer)) != NULL )
|
||||
{
|
||||
LOG( LERR, "[%s] Worker Thread argument malloc failed [%d][%s]", tempBuffer, errorNum, strerror( errorNum ));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "Worker Thread argument malloc failed [%d][%s] and inet_ntop error[%d][%s]", errorNum, strerror( errorNum ), errno, strerror(errno) );
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// 인자로 전달할 변수정보 세팅.
|
||||
pThreadArg->clientSocketDesc = clientSocket;
|
||||
pThreadArg->pLocalIpCache = &localIpCache;
|
||||
pThreadArg->pIpCache = &ipCache;
|
||||
pThreadArg->pProcessStatus = &processStatus;
|
||||
pThreadArg->pProperStorage = &properStorage;
|
||||
}
|
||||
|
||||
// 2. 각 client 요청을 처리할 Thread 생성
|
||||
if( pthread_create( &workerThreadId, NULL, WorkerThreadProc, (void *)pThreadArg) != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
close( clientSocket );
|
||||
free( pThreadArg );
|
||||
|
||||
// Error Logging
|
||||
// CHG 2012-02-01 huibong
|
||||
// inet_ntoa() 함수가 Thread-Safe 하지 않으므로 inet_ntop() 함수로 변경처리
|
||||
//LOG( LERR, "Worker Thread create failed.[%s]->[%d][%s]",inet_ntoa(clientSockAddr.sin_addr), errorNum, strerror( errorNum ));
|
||||
|
||||
char tempBuffer[INET_ADDRSTRLEN];
|
||||
memset(tempBuffer, 0x00, sizeof(tempBuffer));
|
||||
|
||||
if( inet_ntop( AF_INET, &(clientSockAddr.sin_addr), tempBuffer, sizeof(tempBuffer)) != NULL )
|
||||
{
|
||||
LOG( LERR, "[%s] Worker Thread create failed [%d][%s]",tempBuffer, errorNum, strerror( errorNum ));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "Worker Thread create failed [%d][%s] and inet_ntop error[%d][%s]", errorNum, strerror( errorNum ), errno, strerror(errno) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // while( 1 )
|
||||
|
||||
|
||||
// 아래의 코드는 실행되지 않는 코드임.
|
||||
// Process 종료 처리.
|
||||
close( listenSocket );
|
||||
LOG( LWAR, "Worker Process[%d] exit.. Good Bye..", getpid());
|
||||
|
||||
CLogger::Exit();
|
||||
|
||||
// 잠시 대기 후 종료처리.
|
||||
|
||||
// CHG 2015-09-14 huibong
|
||||
// - #24376 usleep -> nanosleep 으로 변경 처리.
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
|
||||
// exit( EXIT_SUCCESS );
|
||||
return EXIT_SUCCESS; // fork 를 수행한 함수에서 exit 함수 호출을 통한 종료처리 (Listener.cpp)
|
||||
}
|
||||
|
||||
|
||||
void * WorkerThreadProc( void * arg)
|
||||
{
|
||||
// 전달받은 인자값을 내부 변수에 저장처리.
|
||||
int clientSocket = ((struct WorkerThreadArg *) arg)->clientSocketDesc;
|
||||
|
||||
// 아래의 정보는 WorkerMain() 에서 생성한 전역관련 정보임.
|
||||
// 전역정보의 생성 및 해제처리는 WorkerMain() 함수에서 처리된다.
|
||||
CLocalIPCache * pLocalIpCache = ((struct WorkerThreadArg *) arg)->pLocalIpCache;
|
||||
CIPCache * pIpCache = ((struct WorkerThreadArg *) arg)->pIpCache;
|
||||
CProcessStatus * pProcessStatus = ((struct WorkerThreadArg *) arg)->pProcessStatus;
|
||||
CProperStorage * pProperStorage = ((struct WorkerThreadArg *) arg)->pProperStorage;
|
||||
|
||||
free( arg ); // 전달 받은 변수를 저장한 동적 생성 버퍼를 해제한다.
|
||||
pthread_detach(pthread_self()); // 메인 함수에서 이 스레드가 종료되기를 기다리지 않게 분리
|
||||
|
||||
LOG( LDBG, "Worker[%d] client[%d] start", getpid(), clientSocket );
|
||||
|
||||
// client 로 부터 전달받은 요청 패킷을 처리할 socket 관련 class 생성
|
||||
CSocketControl socketControl( clientSocket );
|
||||
|
||||
struct timeval timeOver;
|
||||
int result;
|
||||
int receive_result;
|
||||
fd_set selectFds;
|
||||
FD_ZERO( &selectFds );
|
||||
|
||||
// 파일의 송신을 담당하기 위한 FileSender Thread Class 생성
|
||||
CFileSender fileSender( pIpCache, pProcessStatus, &socketControl);
|
||||
|
||||
// Packet Header 정보를 이용한 client 요청 분석
|
||||
bool bRequest;
|
||||
bool bFromControl;
|
||||
char command;
|
||||
|
||||
// Socket 요청 대기 - Server 이므로 client 에서 접속 종료전까지 접속 유지 처리.
|
||||
while(1)
|
||||
{
|
||||
timeOver.tv_sec = DEFAULT_ALIVE_CHECK_TIME;
|
||||
timeOver.tv_usec = 0;
|
||||
FD_SET( clientSocket, &selectFds );
|
||||
|
||||
result = select( clientSocket+1, &selectFds, (fd_set *)NULL, (fd_set *)NULL, &timeOver );
|
||||
|
||||
if( result > 0 )
|
||||
{
|
||||
if( FD_ISSET( clientSocket, &selectFds) )
|
||||
{
|
||||
// Socket 으로 신호가 들어온 경우 ( Data 수신 또는 접속 종료시)
|
||||
receive_result = socketControl.GetHeaderInfo( bRequest, bFromControl, command );
|
||||
|
||||
if( receive_result == -1 )
|
||||
{
|
||||
// Socket이 종료된 경우
|
||||
|
||||
// File Sender Thread 가 동작 중이지 않은 경우 그냥 종료 처리.
|
||||
if( fileSender.IsIdle() == true )
|
||||
{
|
||||
LOG( LDBG, "Worker[%d] client[%d] connection closed => Thread exit", getpid(), clientSocket );
|
||||
break; // 종료 처리.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Control 과의 접속이 종료된 상황에서 File Sender Thread 가 동작중인 경우.
|
||||
// fileSender Thread 를 정지시키고 최대 10초간 대기 후 종료처리를 수행한다.
|
||||
fileSender.Stop();
|
||||
|
||||
for( int i=0; i < 10; i++)
|
||||
{
|
||||
if( fileSender.IsIdle() == true )
|
||||
break;
|
||||
else
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
LOG( LDBG, "Worker[%d] client[%d] connection closed in file sending. Thread exit", getpid(), clientSocket );
|
||||
break; // 종료 처리.
|
||||
}
|
||||
}
|
||||
else if( receive_result == 0 )
|
||||
{
|
||||
// Read Timeout 발생시 => Server Daemon 이므로 client 접속종료시까지 대기
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 정상적으로 데이터를 수신한 경우.
|
||||
// 요청 분석.
|
||||
if( bFromControl == false && bRequest == true )
|
||||
{
|
||||
// Transfer Daemon 간 요청인 경우
|
||||
switch( command )
|
||||
{
|
||||
case TRANSFER_LOCAL_IP: // 1. 내부 NIC IP 요청인 경우
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive TRANSFER_LOCAL_IP", getpid(), clientSocket );
|
||||
ProcessLocalIpRequest( &socketControl, pLocalIpCache);
|
||||
break;
|
||||
|
||||
case TRANSFER_FILE_REPLICATION: // 2. 파일 Replication 수신 요청인 경우
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive TRANSFER_FILE_REPLICATION", getpid(), clientSocket );
|
||||
pProcessStatus->AddReceiveCount();
|
||||
ProcessFileReceive( &socketControl, pProperStorage, command );
|
||||
pProcessStatus->SubtractReceiveCount();
|
||||
break;
|
||||
|
||||
case TRANSFER_FILE_CACHE: // 3. 파일 Cache 수신 요청인 경우
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive TRANSFER_FILE_CACHE", getpid(), clientSocket );
|
||||
pProcessStatus->AddReceiveCount();
|
||||
ProcessFileReceive( &socketControl, pProperStorage, command );
|
||||
pProcessStatus->SubtractReceiveCount();
|
||||
break;
|
||||
|
||||
case TRANSFER_FILE_HASH: // 4. File Hash 검사 요청시.
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive TRANSFER_FILE_HASH", getpid(), clientSocket );
|
||||
pProcessStatus->AddReceiveCount();
|
||||
ProcessFileHashCheck( &socketControl );
|
||||
pProcessStatus->SubtractReceiveCount();
|
||||
break;
|
||||
|
||||
case COMMON_ALIVE_CHECK: // 5. Alive Check 요청인 경우 무시처리.
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive COMMON_ALIVE_CHECK", getpid(), clientSocket );
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG( LERR, "Worker[%d] client[%d] receive [Unknown request from transfer][%02x]", getpid(), clientSocket, command );
|
||||
socketControl.PrintHeaderToLog();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( bFromControl == true && bRequest == true )
|
||||
{
|
||||
// Control Daemon 으로부터 요청인 경우
|
||||
switch( command )
|
||||
{
|
||||
|
||||
case CONTROL_FILE_REPLICATION: // 1. 파일 Replication 처리 요청
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive CONTROL_FILE_REPLICATION", getpid(), clientSocket );
|
||||
ProcessFileSend( fileSender, pProcessStatus, &socketControl, command );
|
||||
break;
|
||||
|
||||
case CONTROL_FILE_CACHE: // 2. 파일 Cache 처리 요청
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive CONTROL_FILE_CACHE", getpid(), clientSocket );
|
||||
ProcessFileSend( fileSender, pProcessStatus, &socketControl, command );
|
||||
break;
|
||||
|
||||
case CONTROL_FILE_CHECK: // 3. Content 에 대한 존재 여부 및 Size, Hash 정보 추출 요청
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive CONTROL_FILE_CHECK", getpid(), clientSocket );
|
||||
ProcessFileCheck( &socketControl, command );
|
||||
break;
|
||||
|
||||
case CONTROL_FILE_UNLINK: // 4. Content 에 대한 unlink 처리 요청
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive CONTROL_FILE_UNLINK", getpid(), clientSocket );
|
||||
ProcessFileUnlink( &socketControl, command );
|
||||
break;
|
||||
|
||||
case COMMON_ALIVE_CHECK: // 5. Alive Check 요청인 경우 무시처리.
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive COMMON_ALIVE_CHECK", getpid(), clientSocket );
|
||||
break;
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460)
|
||||
case CONTROL_FILE_CHECK_TOTO: // 6. 토토디스크 고객사 지원을 위한 MD5 Hash 추출 기능 (2016-05-03 추가)
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive CONTROL_FILE_CHECK_TOTO", getpid(), clientSocket );
|
||||
ProcessFileCheckTOTO( &socketControl, command );
|
||||
break;
|
||||
|
||||
// NEW 2021-04-01 huibong content stat(size, atime, mtime, ctime) 정보 추출 기능 추가 (#33516)
|
||||
case CONTROL_FILE_STAT:
|
||||
LOG( LDBG, "Worker[%d] client[%d] receive CONTROL_FILE_STAT", getpid(), clientSocket );
|
||||
ProcessFileStat( &socketControl, command );
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG( LERR, "Worker[%d] client[%d] receive [Unknown request from control][%02x]", getpid(), clientSocket, command );
|
||||
socketControl.PrintHeaderToLog();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "Worker[%d] client[%d] receive error [Not defined packet header]", getpid(), clientSocket );
|
||||
socketControl.PrintHeaderToLog();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( result == 0 ) // Timeout
|
||||
{
|
||||
// Alive Check Packet send.
|
||||
if( socketControl.SendAliveCheck() == false )
|
||||
{
|
||||
_LOG( LINF, "Worker[%d] client[%d] alive check failed", getpid(), clientSocket );
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // select function 오류.
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "Worker[%d] client[%d] select function error [%d][%s]" , getpid(), clientSocket, errorNum, strerror(errorNum));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 종료 처리.
|
||||
socketControl.Close();
|
||||
|
||||
// Logging
|
||||
LOG( LDBG, "Worker[%d] client[%d] thread end", getpid(), clientSocket );
|
||||
|
||||
// Thread 종료 처리.
|
||||
|
||||
// CHG 2013-03-19 huibong
|
||||
// detach 된 Thread 에 대해 pthread_exit() 호출시
|
||||
// 기준 할당 자원에 대한 해지 처리가 되지 않아 메모리 누수 상황이 발생.
|
||||
// 따라서 이를 해결하기 detach 된 thread 에 대해 pthread_exit() 호출 코드 제거.
|
||||
//pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/// @brief Control 로 부터 Transfer 간 파일 전송(Replication, Cache, Move) 요청이 들어온 경우 이를 처리하기 위한 함수.
|
||||
bool ProcessFileSend( CFileSender& fileSender, CProcessStatus * pProcessStatus, CSocketControl * pSocketControl, char command )
|
||||
{
|
||||
// 1. Control 로 부터 Packet Data 부분을 전달받는다.
|
||||
std::string szSourceFileName;
|
||||
std::string szTargetTranId; // RC 간 동기화시 Target Tran ID 정보를 저장하기 위한 변수
|
||||
unsigned long long nSourceFileSize;
|
||||
int nFileHashCheckLevel;
|
||||
bool bUseInternalIp;
|
||||
std::vector< std::string > vecTargetFhs;
|
||||
|
||||
std::string szErrorMessage; // 오류 메시지 정보를 임시로 저장하기 위한 변수.
|
||||
|
||||
|
||||
if( pSocketControl->GetFileRequestInfo( szSourceFileName, nSourceFileSize, szTargetTranId
|
||||
, nFileHashCheckLevel, bUseInternalIp, vecTargetFhs ) == false )
|
||||
{
|
||||
// Data 정보 수신 실패시 Control 로 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( "File Replication/Cache request info get failed.", command );
|
||||
LOG( LERR, "Worker[%d] File Replication/Cache request info get failed.", getpid() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 현재 Process 에서 MAX File Send Thread 값에 제한이 걸리지 않는지 검사.
|
||||
if( pProcessStatus->GetSendCount() >= g_envConfig.nMaxFileSendThread )
|
||||
{
|
||||
// 오류 메시지를 Socket 으로 전달하고 종료처리.
|
||||
pSocketControl->SendErrorMessageToControl( "Max file send thread count over", command );
|
||||
LOG( LERR, "Worker[%d] Max file send thread count over.", getpid() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 파일 전송을 담당하는 FileSender 가 현재 사용중인지 검사.
|
||||
if( fileSender.IsIdle() == false )
|
||||
{
|
||||
// 오류 메시지를 Socket 으로 전달하고 종료한다.
|
||||
pSocketControl->SendErrorMessageToControl( "Already file send job stated.", command );
|
||||
LOG( LERR, "Worker[%d] Already file send job stated.", getpid());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 요청 정보를 fileSender 에게 설정한다.
|
||||
if( fileSender.Init( szSourceFileName, nSourceFileSize, szTargetTranId, vecTargetFhs, nFileHashCheckLevel, bUseInternalIp, command, szErrorMessage ) == false )
|
||||
{
|
||||
// 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d] Request or Source file info not valid.[%s]", getpid(), szErrorMessage.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. fileSender 에게 작업 시작을 알리고 반환처리한다.
|
||||
// 나머지 작업은 fileSender 에서 내부적으로 처리해 버린다.
|
||||
fileSender.Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// @brief Local NIC IP 요청에 대한 처리를 수행한다.
|
||||
/// @param pSocketControl [in] 요청 Client socket 에 대한 CSocketControl object pointer
|
||||
/// @param pLocalIpCache [in] Local NIC IP 정보 처리를 위한 CLocalIPCache object pointer
|
||||
/// @return Local NIC 처리결과 전송 성공시 true, 실패시 false 반환.
|
||||
bool ProcessLocalIpRequest( CSocketControl * pSocketControl, CLocalIPCache * pLocalIpCache )
|
||||
{
|
||||
string szLocalIp;
|
||||
bool bResult;
|
||||
|
||||
// Local NIC IP 정보 확인.
|
||||
bResult = pLocalIpCache->GetLocalIP( szLocalIp );
|
||||
if( bResult == false )
|
||||
{
|
||||
LOG( LWAR, "Worker[%d]: Local NIC IP get failed.", getpid() );
|
||||
}
|
||||
|
||||
// 해당 NIC 정보 전송
|
||||
if( pSocketControl->SendLocalIpInfo( bResult, szLocalIp ) == true )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief Replication 대상 파일의 수신 요청에 대한 처리를 수행한다.
|
||||
/// @param pSocketControl [in] 요청 Client socket 에 대한 CSocketControl object pointer
|
||||
/// @param pProperStorage [in] CProperStorage Object Pointer.
|
||||
/// @param commandCode [in] Replication 또는 Cache 요청인지 여부값 (TRANSFER_FILE_REPLICATION, TRANSFER_FILE_CACHE)
|
||||
/// @return 파일 수신 및 처리 성공시 true, 오류 발생시 false 반환.
|
||||
bool ProcessFileReceive( CSocketControl * pSocketControl, CProperStorage * pProperStorage, char commandCode )
|
||||
{
|
||||
string szProperStorage;
|
||||
unsigned long long nMaxAvailableSize;
|
||||
|
||||
// 2014-03-04 CHG huibong
|
||||
// CProperStorage 객체 수정에 따른 처리 코드 변경함.
|
||||
// - CProperStorage::GetProperPath() 함수는 적합한 저장 경로를 추출하지 못한 경우... 빈값을 반환한다.
|
||||
// - 하지만 본 함수에서는 socket 을 통해 오류 내역을 전달할 수 없으므로... socket 처리 함수에서 이를 처리토록 한다.
|
||||
|
||||
// 1. 전달받은 파일에 대해 저장할 위치를 검색한다.
|
||||
// - 본 함수의 인자로 전달받은 CProperStorage 객체 포인터는 NULL 이 될 수 없다.
|
||||
pProperStorage->GetProperPath( szProperStorage, nMaxAvailableSize );
|
||||
|
||||
// 2. 만약 Cache 처리요청인 경우
|
||||
// 위에서 구한 경로 하위 cache 폴더로 저장되도록 저장경로를 수정처리한다.
|
||||
// 즉 /stg/node1/cache/261/abdeesss 로 처리되도록 한다.
|
||||
if( commandCode == TRANSFER_FILE_CACHE && szProperStorage.empty() == false )
|
||||
{
|
||||
szProperStorage += "/cache";
|
||||
}
|
||||
|
||||
|
||||
// 3. SocketControl 에서 해당 내역을 처리하도록 전달처리.
|
||||
if( pSocketControl->ReceiveFile( szProperStorage, nMaxAvailableSize, commandCode ) == false )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcessFileHashCheck( CSocketControl * pSocketControl )
|
||||
{
|
||||
// ScocketControl 에게 파일 정보 및 Hash Level, Hash 값 정보를 전달받아
|
||||
// Hash 를 검사하도록 처리한다.
|
||||
if( pSocketControl->CheckFileHash( ) == false )
|
||||
{
|
||||
LOG( LERR, "Worker[%d]: Reciever hash check fail ", getpid());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Control 로 부터 Content 에 대한 Check ( Size, Hash ) 요청을 받은 경우 이를 처리하기 위한 함수
|
||||
/// @param
|
||||
/// @retrun
|
||||
bool ProcessFileCheck( CSocketControl * pSocketControl, char command )
|
||||
{
|
||||
// 1. Control 로 부터 Packet Data 부분을 전달받는다.
|
||||
std::string szFileName;
|
||||
bool bHashCheck;
|
||||
unsigned long long nHashCheckSize; // MByte 단위값.
|
||||
|
||||
std::string szErrorMessage; // 오류 메시지 정보를 임시로 저장하기 위한 변수.
|
||||
|
||||
if( pSocketControl->GetRequestCheckFromControl( szFileName, bHashCheck, nHashCheckSize ) == false )
|
||||
{
|
||||
// Data 정보 수신 실패시 Control 로 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( "File Check request info get failed.", command );
|
||||
LOG( LERR, "Worker[%d] File Check request info get failed.", getpid() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 결과값을 저장하기 위한 변수 초기화
|
||||
unsigned long long nFileSize = 0;
|
||||
std::string szHashValue;
|
||||
|
||||
// 3. 전달받은 파일이 존재하는지 검사.
|
||||
if( access(szFileName.c_str(), R_OK) == -1 )
|
||||
{
|
||||
szErrorMessage = "Check file access fail.[" + szFileName + "]";
|
||||
|
||||
// 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: %s", getpid(), szErrorMessage.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 파일 크기값 추출
|
||||
FILE * pFile = fopen( szFileName.c_str(), "r+" );
|
||||
if( pFile == NULL )
|
||||
{
|
||||
szErrorMessage = "Check file open fail.[" + szFileName + "]";
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: %s", getpid(), szErrorMessage.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseeko( pFile, 0, SEEK_END );
|
||||
off_t totalSize = ftello( pFile );
|
||||
rewind( pFile );
|
||||
fclose( pFile ); // 열려진 descriptor close
|
||||
|
||||
nFileSize = (unsigned long long)totalSize; // 파일 Size 추출 완료
|
||||
}
|
||||
|
||||
// 5. Hash 값 추출인 경우
|
||||
if( bHashCheck == true )
|
||||
{
|
||||
// Hash 처리를 위한 객체를 생성한 후 Hash 검사를 수행
|
||||
CFileHash fileHash;
|
||||
|
||||
if( nHashCheckSize == 0 ) // 전체 Size 에 대한 Hash 추출인 경우...
|
||||
{
|
||||
if( fileHash.GetFileHash( szFileName, HASH_LEVEL_FULL, szHashValue) == false )
|
||||
{
|
||||
szErrorMessage = "Hash(full) create fail [" + fileHash.GetLastErrorMessage() + "]";
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: %s", getpid(), szErrorMessage.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Content 앞부분에 대한 일부 Size 에 Hash 추출인 경우... 추출 Size 설정
|
||||
fileHash.SetPartitialSize( nHashCheckSize );
|
||||
|
||||
if( fileHash.GetFileHash( szFileName, HASH_LEVEL_FRONT_PARTITIAL, szHashValue) == false )
|
||||
{
|
||||
szErrorMessage = "Hash(front) create fail [" + fileHash.GetLastErrorMessage() + "]";
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: %s", getpid(), szErrorMessage.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 추출된 정보를 전달한다.
|
||||
if( pSocketControl->SendCheckResultToControl( command, nFileSize, szHashValue ) == false )
|
||||
{
|
||||
// 실패 발생시 로깅 처리.
|
||||
LOG( LERR, "Worker[%d] File[%s] Check result[%llu] send fail.", getpid(), szFileName.c_str(), nFileSize );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 7. 처리 내역 로깅
|
||||
_LOG( LINF, "File[%s] check complete. size[%llu] hash[%s]", szFileName.c_str(), nFileSize, szHashValue.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460, #27533)
|
||||
/// @brief 고객사 토토디스크 지원을 위한 기능
|
||||
/// Control 로 부터 전달받은 Content 에 대해 Size 정보 및 MD5 Hash, Checksum 정보 등을 추출하여 전달한다.
|
||||
/// @param
|
||||
/// @retrun
|
||||
bool ProcessFileCheckTOTO( CSocketControl * pSocketControl, char command )
|
||||
{
|
||||
// 1. Control 로 부터 Packet Data 부분을 전달받는다.
|
||||
std::string szFileName;
|
||||
std::string szErrorMessage; // 오류 메시지 정보를 임시로 저장하기 위한 변수.
|
||||
|
||||
if( pSocketControl->GetRequestCheckTOTOFromControl( szFileName ) == false )
|
||||
{
|
||||
// Data 정보 수신 실패시 Control 로 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( "File Check request info get failed.", command );
|
||||
LOG( LERR, "Worker[%d]: [TOTO] File Check request info get failed.", getpid() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 결과값을 저장하기 위한 변수 초기화
|
||||
unsigned long long nFileSize = 0;
|
||||
std::string szResultTotoMd5;
|
||||
std::string szResultChecksum;
|
||||
std::string szResultDnaCheckKey;
|
||||
|
||||
|
||||
// 3. 전달받은 파일이 존재하는지 검사.
|
||||
if( access( szFileName.c_str(), R_OK ) == -1 )
|
||||
{
|
||||
szErrorMessage = "Check file access fail.[" + szFileName + "]";
|
||||
|
||||
// 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: [TOTO] %s", getpid(), szErrorMessage.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 파일 크기값 추출
|
||||
FILE * pFile = fopen( szFileName.c_str(), "r+" );
|
||||
if( pFile == NULL )
|
||||
{
|
||||
szErrorMessage = "Check file open fail.[" + szFileName + "]";
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: [TOTO] %s", getpid(), szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseeko( pFile, 0, SEEK_END );
|
||||
off_t totalSize = ftello( pFile );
|
||||
rewind( pFile );
|
||||
fclose( pFile ); // 열려진 descriptor close
|
||||
|
||||
nFileSize = (unsigned long long)totalSize; // 파일 Size 추출 완료
|
||||
}
|
||||
|
||||
|
||||
// 5. 토토측 MD5 및 DNA Check key 추출 함수 호출
|
||||
CFileHash fileHash;
|
||||
if( fileHash.GetHashTOTO( szFileName, nFileSize, szResultTotoMd5, szResultDnaCheckKey ) == false )
|
||||
{
|
||||
szErrorMessage = "MD5 extract fail [" + fileHash.GetLastErrorMessage() + "]";
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: [TOTO] %s", getpid(), szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. 토토측 Checksum 추출 함수 호출.
|
||||
CTotoChecksum totoChecksum;
|
||||
if( totoChecksum.GetChecksum( szFileName, nFileSize, szResultChecksum ) == false )
|
||||
{
|
||||
szErrorMessage = "Checksum extract fail [" + totoChecksum.GetErrorMessage() + "]";
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: [TOTO] %s", getpid(), szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 7. 추출된 정보를 전달한다.
|
||||
if( pSocketControl->SendTOTOCheckResultToControl( command, nFileSize, szResultTotoMd5, szResultChecksum, szResultDnaCheckKey ) == false )
|
||||
{
|
||||
// 실패 발생시 로깅 처리.
|
||||
LOG( LERR, "Worker[%d] [TOTO] File[%s] Check size[%llu] & result send fail.", getpid(), szFileName.c_str(), nFileSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 8. 처리 내역 로깅
|
||||
_LOG( LINF, "[TOTO] File[%s] check complete. size[%llu] totomd5[%s] checksum[%s] dnakey[%s]"
|
||||
, szFileName.c_str(), nFileSize, szResultTotoMd5.c_str(), szResultChecksum.c_str(), szResultDnaCheckKey.c_str() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @brief Control 로 부터 Content 에 대한 Unlink 요청을 받은 경우 이를 처리하기 위한 함수
|
||||
/// @param
|
||||
/// @retrun
|
||||
bool ProcessFileUnlink( CSocketControl * pSocketControl, char command )
|
||||
{
|
||||
// 1. Control 로 부터 Packet Data 부분을 전달받는다.
|
||||
std::string szFileName;
|
||||
unsigned long long nFileSize;
|
||||
unsigned int nUnlinkMode = 0;
|
||||
|
||||
std::string szErrorMessage; // 오류 메시지 정보를 임시로 저장하기 위한 변수.
|
||||
|
||||
if( pSocketControl->GetRequestUnlinkFromControl( szFileName, nFileSize, nUnlinkMode ) == false )
|
||||
{
|
||||
// Data 정보 수신 실패시 Control 로 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( "File Unlink request info get failed.", command );
|
||||
LOG( LERR, "Worker[%d] File Unlink request info get failed.", getpid() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 전달받은 파일이 존재하는지 검사.
|
||||
// - 일반 모드 : 해당 파일이 존재하지 않을 경우 오류로 처리...
|
||||
// - 강제 모드 : 해당 파일이 존재하지 않을 경우.. 이미 삭제된 것으로 판단하여 정상으로 처리.
|
||||
if( access(szFileName.c_str(), R_OK) == -1 )
|
||||
{
|
||||
if( nUnlinkMode == UNLINK_MODE_FORCE )
|
||||
{
|
||||
// 강제 삭제 모드인 경우 해당 파일이 존재하지 않으므로.. 정상으로 처리..
|
||||
// 로깅은 필히 남길 것.
|
||||
_LOG( LWAR, "File[%s][[%llu] not found. but unlink result success by unlink force mode", szFileName.c_str(), nFileSize);
|
||||
|
||||
// 처리 결과 전달
|
||||
if( pSocketControl->SendUnlinkResultToControl( command ) == false )
|
||||
{
|
||||
// 실패 발생시 로깅 처리.
|
||||
LOG( LERR, "Worker[%d] file[%s] unlink result send fail.", getpid(), szFileName.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 강제 삭제 모드가 아닌 경우.. 해당 파일이 존재하지 않으므로 실패로 처리.
|
||||
szErrorMessage = "unlink file not found.[" + szFileName + "]";
|
||||
|
||||
// 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: %s", getpid(), szErrorMessage.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 파일 크기값 추출
|
||||
FILE * pFile = fopen( szFileName.c_str(), "r+" );
|
||||
if( pFile == NULL )
|
||||
{
|
||||
szErrorMessage = "Unlink file open fail.[" + szFileName + "]";
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: %s", getpid(), szErrorMessage.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseeko( pFile, 0, SEEK_END );
|
||||
off_t totalSize = ftello( pFile );
|
||||
rewind( pFile );
|
||||
fclose( pFile ); // 열려진 descriptor close
|
||||
|
||||
if( (unsigned long long)totalSize != nFileSize )
|
||||
{
|
||||
// std::string 에 숫자를 append 처리시 오류가 자주 발생하여 C Style 로 변환하여 처리함.
|
||||
char tempBuffer[1024];
|
||||
snprintf( tempBuffer, sizeof(tempBuffer)-1
|
||||
, "unlink file[%s] size not match.[request:%llu, local:%jd]"
|
||||
, szFileName.c_str(), nFileSize, totalSize );
|
||||
|
||||
if( nUnlinkMode == UNLINK_MODE_FORCE )
|
||||
{
|
||||
// 강제 삭제 모드인 경우.. 관련 내역을 로깅 처리만 수행
|
||||
_LOG( LWAR, "%s. but unlink force mode", tempBuffer );
|
||||
}
|
||||
else
|
||||
{
|
||||
// 일반 모드인 경우 Size 가 서로 다르므로.. 실패로 처리
|
||||
szErrorMessage = tempBuffer;
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: %s", getpid(), szErrorMessage.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. unlink 처리
|
||||
if( unlink( szFileName.c_str() ) != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
|
||||
char tempBuffer[1024];
|
||||
snprintf( tempBuffer, sizeof(tempBuffer)-1
|
||||
, "Unlink [%s][%llu][%d] system error[%d][%s]"
|
||||
, szFileName.c_str(), nFileSize, nUnlinkMode, errorNum, strerror(errorNum) );
|
||||
|
||||
szErrorMessage = tempBuffer;
|
||||
pSocketControl->SendErrorMessageToControl( szErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: %s", getpid(), szErrorMessage.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. 처리 결과를 전달한다.
|
||||
if( pSocketControl->SendUnlinkResultToControl( command ) == false )
|
||||
{
|
||||
// 실패 발생시 로깅 처리.
|
||||
LOG( LERR, "Worker[%d] File[%s] unlink result send fail.", getpid(), szFileName.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 7. 로깅
|
||||
if( nUnlinkMode == UNLINK_MODE_FORCE )
|
||||
{
|
||||
_LOG( LINF, "File[%s][%llu] unlink complete with force mode.", szFileName.c_str(), nFileSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LINF, "File[%s][%llu] unlink complete.", szFileName.c_str(), nFileSize );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// NEW 2021-04-01 huibong content stat(size, atime, mtime, ctime) 정보 추출 기능 추가 (#33516)
|
||||
bool ProcessFileStat( CSocketControl * pSocketControl, char command )
|
||||
{
|
||||
// 1. Control 로 부터 Packet Data 부분을 전달받는다.
|
||||
std::string strFileName;
|
||||
|
||||
// 오류 메시지 정보를 임시로 저장하기 위한 변수.
|
||||
std::string strErrorMessage;
|
||||
|
||||
if( pSocketControl->GetRequestStatFromControl( strFileName ) == false )
|
||||
{
|
||||
// Data 정보 수신 실패시 Control 로 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( "file stat request receive failed.", command );
|
||||
LOG( LERR, "Worker[%d] file stat request receive failed.", getpid() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 전달받은 파일이 존재하는지 검사.
|
||||
if( access( strFileName.c_str(), R_OK ) == -1 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
//strErrorMessage = "file access error [" + strFileName + "][" + strerror( errorNum ) + "]";
|
||||
strErrorMessage = strerror( errorNum );
|
||||
|
||||
// 오류 메시지를 전송한다.
|
||||
pSocketControl->SendErrorMessageToControl( strErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: stat access error [%s][%s]", getpid(), strFileName.c_str(), strErrorMessage.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 3. 요청 파일에 대한 stat 정보를 추출한다.
|
||||
|
||||
struct stat stFileStat;
|
||||
if( stat( strFileName.c_str(), &stFileStat ) != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
//strErrorMessage = "stat function error [" + strFileName + "][" + strerror( errorNum ) + "]";
|
||||
strErrorMessage = strerror( errorNum );
|
||||
|
||||
pSocketControl->SendErrorMessageToControl( strErrorMessage, command );
|
||||
LOG( LERR, "Worker[%d]: stat function error [%s][%s]", getpid(), strFileName.c_str(), strErrorMessage.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 추출된 정보를 전달한다.
|
||||
|
||||
if( pSocketControl->SendStatResultToControl( command
|
||||
, stFileStat.st_size, stFileStat.st_atime, stFileStat.st_mtime, stFileStat.st_ctime ) == false )
|
||||
{
|
||||
// 실패 발생시 로깅 처리.
|
||||
LOG( LERR, "Worker[%d] file[%s] stat result send fail. [ size:%ld access:%ld modify:%ld change:%ld ]"
|
||||
, getpid(), strFileName.c_str()
|
||||
, stFileStat.st_size, stFileStat.st_atime, stFileStat.st_mtime, stFileStat.st_ctime );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 7. 처리 내역 로깅
|
||||
_LOG( LINF, "file[%s] stat complete.[ size:%ld access:%ld modify:%ld change:%ld ]"
|
||||
, strFileName.c_str(), stFileStat.st_size, stFileStat.st_atime, stFileStat.st_mtime, stFileStat.st_ctime );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// @brief Worker Process 종료 Signal 을 전달받은 경우 이를 처리하기 위한 함수.
|
||||
/// @param nSignalNumber [in] 발생한 시그널 Number
|
||||
/// @return void
|
||||
static void SigTermWorker( int nSignalNumber )
|
||||
{
|
||||
// Signal Number 에 따른 로깅처리.
|
||||
if( nSignalNumber == SIGTERM )
|
||||
{
|
||||
LOG( LWAR, "Worker Process[%d] exit job start by user signal [SIGTERM]", getpid() );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LWAR, "Worker Process[%d] exit job start by abnormal signal [%d]", getpid(), nSignalNumber );
|
||||
}
|
||||
|
||||
// Process 종료관련 작업 추가
|
||||
close( g_listenSocket );
|
||||
|
||||
LOG( LWAR, "Worker Process[%d] exit job end. Good Bye..", getpid());
|
||||
|
||||
CLogger::Exit();
|
||||
|
||||
// CHG 2015-09-14 huibong
|
||||
// - #24376 usleep -> nanosleep 으로 변경 처리.
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
/// @brief Worker 프로세스 signal 처리 설정을 위한 함수
|
||||
/// @return void
|
||||
void SetSignalWorker( void )
|
||||
{
|
||||
sigset_t set;
|
||||
struct sigaction act;
|
||||
|
||||
sigfillset( &set );
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
|
||||
memset( &act, 0x00, sizeof(act) );
|
||||
sigfillset( &act.sa_mask );
|
||||
|
||||
/* 무시할 신호 목록 */
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction( SIGPIPE, &act, NULL); /* 파이프 디스크립터 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
|
||||
sigaction( SIGHUP , &act, NULL); /* Process를 기동시킨 관리자의 로그아웃시 발생 시그널 */
|
||||
sigaction( SIGINT , &act, NULL); /* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
|
||||
sigaction( SIGQUIT, &act, NULL); /* 키보드에 의한 Abort 신호 처리 => ? */
|
||||
|
||||
// Worker 프로세스는 자식 프로세스가 존재하지 않으므로 그냥 무시처리함.
|
||||
//act.sa_handler = SigChldWorker;
|
||||
sigaction( SIGCHLD, &act, NULL);
|
||||
|
||||
/* 각종 에러나 사용자의 종료 신호 처리 */
|
||||
act.sa_handler = SigTermWorker;
|
||||
sigaction( SIGTERM, &act, NULL); /* kill -TERM 에 의한 프로세스 종료시 */
|
||||
|
||||
/* Core 관련 signal 처리 : 오류 처리 및 Debug(core dump) 목적 */
|
||||
|
||||
// CHG 2012-05-21 huibong
|
||||
// Worker 프로세스의 문제 발생시 문제점 해결을 위한 core 파일 분석을 위해.. signal handler 를 기본으로 변경 처리함.
|
||||
act.sa_handler = SIG_DFL;
|
||||
sigaction( SIGILL , &act, NULL); /* Illegal instruction */
|
||||
sigaction( SIGFPE , &act, NULL); /* Erroneout arithmetic operation */
|
||||
sigaction( SIGBUS , &act, NULL); /* Access to undefined portion of a memory object */
|
||||
sigaction( SIGSEGV, &act, NULL); /* Invalid memory reference */
|
||||
sigaction( SIGSYS , &act, NULL); /* Bad System Call */
|
||||
sigaction( SIGXCPU, &act, NULL); /* CPU-time limit exceeded */
|
||||
sigaction( SIGXFSZ, &act, NULL); /* File-size limit exceeded */
|
||||
|
||||
sigemptyset(&set); /* 신호 처리기 처리 설정 위한 블록 해제 */
|
||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/***************************************************************************
|
||||
ftsd ( File TranSfer Daemon ) Header ( Worker.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/04
|
||||
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 __WORKER_H__
|
||||
#define __WORKER_H__
|
||||
|
||||
#include "../define_ftsd.h"
|
||||
|
||||
#include "LocalIPCache.h"
|
||||
#include "IPCache.h"
|
||||
#include "ProcessStatus.h"
|
||||
#include "ProperStorage.h"
|
||||
#include "SocketControl.h"
|
||||
#include "FileSender.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// 이거는 malloc 을 사용해야 하므로 C++ 스타일의 구조체를 만들지 않는다.
|
||||
struct WorkerThreadArg {
|
||||
|
||||
int clientSocketDesc; // Client Socket Descriptor
|
||||
|
||||
CLocalIPCache * pLocalIpCache; // Local NIC IP Cache Calss pointer
|
||||
CIPCache * pIpCache; // 내부망 IP 정보 Cache Class pointer
|
||||
CProcessStatus * pProcessStatus; // Process 상태 정보 Class pointer
|
||||
CProperStorage * pProperStorage; // 내부 저장 stroage 정보 Class pointer
|
||||
};
|
||||
|
||||
/// @brief Worker Process 의 main 함수
|
||||
/// @param listenSocket [in] Listen Socket Descriptor
|
||||
/// @return
|
||||
int WorkerMain( const int listenSocket );
|
||||
|
||||
/// @brief 각 요청을 처리하기 위함 Thread 함수.
|
||||
/// @param arg [in] struct WorkerThreadArg 에 대한 포인터.
|
||||
/// @return
|
||||
void * WorkerThreadProc( void * arg );
|
||||
|
||||
/// @brief Local NIC IP 요청에 대한 처리를 수행한다.
|
||||
/// @param pSocketControl [in] 요청 Client socket 에 대한 CSocketControl object pointer
|
||||
/// @param pLocalIpCache [in] Local NIC IP 정보 처리를 위한 CLocalIPCache object pointer
|
||||
/// @return Local NIC 처리결과 전송 성공시 true, 실패시 false 반환.
|
||||
bool ProcessLocalIpRequest( CSocketControl * pSocketControl, CLocalIPCache * pLocalIpCache );
|
||||
|
||||
/// @brief Replication 대상 파일의 수신 요청에 대한 처리를 수행한다.
|
||||
/// @param pSocketControl [in] 요청 Client socket 에 대한 CSocketControl object pointer
|
||||
/// @param pProperStorage [in] CProperStorage Object Pointer. 해당값이 NULL 인 경우 Cache, NULL 인 아닌 경우 Replication 처리
|
||||
/// @return 파일 수신 및 처리 성공시 true, 오류 발생시 false 반환.
|
||||
bool ProcessFileReceive( CSocketControl * pSocketControl, CProperStorage * pProperStorage, char commandCode );
|
||||
|
||||
|
||||
bool ProcessFileHashCheck( CSocketControl * pSocketControl );
|
||||
|
||||
|
||||
/// @brief 파일 전송 관련 작업을 수행한다.
|
||||
bool ProcessFileSend( CFileSender& fileSender, CProcessStatus * pProcessStatus
|
||||
, CSocketControl * pSocketControl, char command );
|
||||
|
||||
|
||||
/// @brief Control 로 부터 Content 에 대한 Check ( Size, Hash ) 요청을 받은 경우 이를 처리하기 위한 함수
|
||||
bool ProcessFileCheck( CSocketControl * pSocketControl, char command );
|
||||
|
||||
/// @brief Control 로 부터 Content 에 대한 Unlink 요청을 받은 경우 이를 처리하기 위한 함수
|
||||
bool ProcessFileUnlink( CSocketControl * pSocketControl, char command );
|
||||
|
||||
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460, #27533)
|
||||
/// @brief 고객사 토토디스크 지원을 위한 기능
|
||||
/// Control 로 부터 전달받은 Content 에 대해 Size 정보 및 MD5 Hash, Checksum 정보 등을 추출하여 전달한다.
|
||||
/// @param
|
||||
/// @retrun
|
||||
bool ProcessFileCheckTOTO( CSocketControl * pSocketControl, char command );
|
||||
|
||||
|
||||
// NEW 2021-04-01 huibong content stat(size, atime, mtime, ctime) 정보 추출 기능 추가 (#33516)
|
||||
/// @brief Control 로 부터 Content 에 대한 stat ( size, atime, mtime, ctime ) 요청을 받은 경우 이를 처리하기 위한 함수
|
||||
bool ProcessFileStat( CSocketControl * pSocketControl, char command );
|
||||
|
||||
|
||||
/// @brief Worker 프로세스 signal 처리 설정을 위한 함수
|
||||
/// @return void
|
||||
void SetSignalWorker( void );
|
||||
|
||||
/* Signal 처리를 위한 각 Signal Handler 함수는
|
||||
* 다른 Code 에서 Include 처리시 Static 관련 문제로 인해
|
||||
* 본 Header 에서 선언처리 하지 않음. cpp 에만 존재
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __WORKER_H__ */
|
||||
Reference in New Issue
Block a user