base
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
Revision 1533
|
||||
-------------------
|
||||
수정일 : 2021-03-17
|
||||
수정자 : 유희곤
|
||||
|
||||
- NEW: SVN 신규 등록
|
||||
- 신규 rc_apid 에 대한 SVN 등록
|
||||
- 본 모듈 관련 자세한 사항 및 설계 내역은 redmine #33479 이슈 참고
|
||||
-- content 에 대한 size, hash 추출용 worker 를 우선 개발함.
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#****************************************************************************
|
||||
# Makefile for rc_apid
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2021/03/09
|
||||
# copyright : (C) 2005 Solbox.Inc
|
||||
# author : Dev Storage Team
|
||||
# email : huibong@solbox.com
|
||||
# version : 3.5.0
|
||||
#
|
||||
# CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
# Redistribution and use in source and binary forms, with or with out
|
||||
# modification, are not permitted in outside of Solbox Inc.
|
||||
#*****************************************************************************
|
||||
|
||||
SUBDIRS = lib src
|
||||
|
||||
.PHONY: all $(SUBDIRS)
|
||||
|
||||
|
||||
all: $(SUBDIRS)
|
||||
sync;
|
||||
|
||||
|
||||
$(SUBDIRS):
|
||||
$(MAKE) all -C $@
|
||||
|
||||
|
||||
install:
|
||||
@for dir in $(SUBDIRS); do (cd $$dir; $(MAKE) install); done
|
||||
|
||||
|
||||
clean:
|
||||
@for dir in $(SUBDIRS); do (cd $$dir; $(MAKE) clean); done
|
||||
|
||||
|
||||
# End of Makefile
|
||||
@@ -0,0 +1,67 @@
|
||||
/***************************************************************************
|
||||
ftsd control interface ( File Replication & Cache & Move & Delete Control) Header ( FtsdProtocol.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/09
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 3.2.0.R0811
|
||||
|
||||
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 __FTSD_PROTOCOL_H__
|
||||
#define __FTSD_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 )
|
||||
};
|
||||
|
||||
|
||||
// Packet Header stx 코드
|
||||
#define HEADER_STX_CODE 0x02
|
||||
|
||||
// Packet Header type 구분코드
|
||||
#define HEADER_TYPE_REQUEST 0x00
|
||||
#define HEADER_TYPE_RESPONSE 0x01
|
||||
|
||||
|
||||
// Packet command 공통.
|
||||
#define COMMON_ALIVE_CHECK 0x7F
|
||||
|
||||
// Packet command : control 프로세스 <-> ftsd 통신 ( Command 첫번째 Byte 만 사용 )
|
||||
#define NOT_CONTROL_COMMAND 0x00 // Control 이 전송한 요청이 아닌 경우.
|
||||
#define CONTROL_FILE_REPLICATION 0x01 // Content 복제 생성 요청
|
||||
#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 추가)
|
||||
|
||||
|
||||
// Packet Result : type이 Reponse 인 경우에만 세팅됨.( 첫번째 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 /* __FTSD_PROTOCOL_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
/***************************************************************************
|
||||
ftsd control interface ( File Replication & Cache & Move & Delete Control) Header ( FtsdSocketControl.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/09
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 3.2.0.R0811
|
||||
|
||||
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 __FTSD_SOCKET_CONTROL_H__
|
||||
#define __FTSD_SOCKET_CONTROL_H__
|
||||
|
||||
#include "BaseSocket.h"
|
||||
#include "FtsdProtocol.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
///< BYTE 타입 정의
|
||||
#ifndef _BYTE_DEFINED
|
||||
#define _BYTE_DEFINED
|
||||
typedef unsigned char BYTE;
|
||||
#endif // _BYTE_DEFINED
|
||||
|
||||
|
||||
#define DEFAULT_SOCKET_TEMP_BUFFER_SIZE 1024 // SocketControl 에서 사용할 임시버퍼 크기.
|
||||
|
||||
class CFtsdSocketControl : 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 생성자.
|
||||
CFtsdSocketControl();
|
||||
|
||||
/// @brief 소멸자.
|
||||
~CFtsdSocketControl();
|
||||
|
||||
|
||||
/// @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 대상과 연결된 Socket 을 통해 ftsd 로 File Replication 명령 전송.
|
||||
/// @param szFileName [in] 복제할 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @param nFileSize [in] 복제할 원본 Source 파일의 크기.
|
||||
/// @param szTargetTranId [in] RC 간 복제 처리시 변경할 대상 Tran ID 정보 값.
|
||||
/// @param nFileHashCheckLevel [in] File 에 대한 Hash Check Level 정보 ( conf 파일에 지정됨)
|
||||
/// @param bUseInternalIp [in] 내부망을 이용하여 파일 송수신을 수행할지 여부 \n
|
||||
///< 해당값을 true 로 지정시 ftsd 상에서 내부망을 우선 사용하여 파일 복제 시도
|
||||
///< 만약 내부망 사용 불가시 자동으로 외부망 사용.
|
||||
///< RC-RC 간 처리시에는 사용하지 않도록 false 로 지정할 것.
|
||||
/// @param vecTargetFhs [in] 복제 대상 Target FHS Host Name 정보
|
||||
/// @return Replication 복제 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileReplicationRequest(const std::string& szFileName, unsigned long long nFileSize, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs);
|
||||
bool SendFileReplicationRequest(const std::string& szFileName, unsigned long long nFileSize, const std::string& szTargetTranId, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs);
|
||||
|
||||
|
||||
/// @brief File Replication 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File Replication 이 정상적으로 수행되었는지 여부 \n
|
||||
///< trnsfer 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 \n
|
||||
///< mapSuccessFhs, mapFailFhs 상에 관련 정보가 저장됨.
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @param mapSuccessFhs [out] bSuccess == true 인 경우 \n
|
||||
///< Replication 처리에 성공한 FHS 및 저장된 File 이름 정보를 저장
|
||||
/// @param mapFailFhs [out] bSuccess == true 인 경우 \n
|
||||
///< Replication 처리에 실패한 FHS Host Name 및 발생된 오류메시지 정보를 저장.
|
||||
///
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 로부터
|
||||
///< Replication 에 대한 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 복제 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 로 부터 Data 을 수신하였으나 File Replication 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileReplicationResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage, std::map< std::string, std::string>& mapSuccessFhs, std::map< std::string, std::string>& mapFailFhs );
|
||||
|
||||
|
||||
/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 File Cache 명령 전송.
|
||||
/// @param szFileName [in] Cache 처리할 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @param nFileSize [in] Cache 처리할 원본 Source 파일의 크기.
|
||||
/// @param nFileHashCheckLevel [in] File 에 대한 Hash Check Level 정보 ( conf 파일에 지정됨)
|
||||
/// @param bUseInternalIp [in] 내부망을 이용하여 파일 송수신을 수행할지 여부 \n
|
||||
///< 해당값을 true 로 지정시 ftsd 상에서 내부망을 우선 사용하여 파일 복제 시도
|
||||
///< 만약 내부망 사용 불가시 자동으로 외부망 사용.
|
||||
/// @param vecTargetFhs [in] Cache 대상 Target FHS Host Name 정보
|
||||
/// @return Cache 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileCacheRequest(const std::string& szFileName, unsigned long long nFileSize, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs);
|
||||
|
||||
|
||||
/// @brief File Cache 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File Cache 처리가 정상적으로 수행되었는지 여부 \n
|
||||
///< trnsfer 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 \n
|
||||
///< mapSuccessFhs, mapFailFhs 상에 관련 정보가 저장됨.
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @param mapSuccessFhs [out] bSuccess == true 인 경우 \n
|
||||
///< Cache 처리에 성공한 FHS 및 저장된 File 이름 정보를 저장
|
||||
/// @param mapFailFhs [out] bSuccess == true 인 경우 \n
|
||||
///< Cache 처리에 실패한 FHS Host Name 및 발생된 오류메시지 정보를 저장.
|
||||
///
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 로부터
|
||||
///< Cache 에 대한 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 Cache 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 로 부터 Data 을 수신하였으나 File Cache 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileCacheResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage, std::map< std::string, std::string>& mapSuccessFhs, std::map< std::string, std::string>& mapFailFhs );
|
||||
|
||||
|
||||
/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Content Check 명령 전송.
|
||||
/// @param szFileName [in] 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @param bHashCheck [in] Hash 값을 추출할지 여부
|
||||
/// @param nHashCheckSize [in] Hash 값을 추출할 경우 Size 설정값 ( MByte 단위 )\n
|
||||
///< 0 : Content 에 대한 전체 Hash 값을 추출함.
|
||||
///< 숫자 : Content 의 앞 부분부터 지정된 크기 (MByte ) 까지 Hash 값을 추출
|
||||
/// @return Check 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileCheckRequest(const std::string& szFileName, bool bHashCheck, unsigned long long nHashCheckSize );
|
||||
|
||||
|
||||
|
||||
/// @brief File Check 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File Check 처리가 정상적으로 수행되었는지 여부 \n
|
||||
///< ftsd 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 \n
|
||||
///< nFileSize, szHashValue 상에 결과 정보가 저장됨.
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @param nFileSize [out] bSuccess == true 인 경우 요청한 Content 에 대한 file size 정보를 저장. ( Byte 단위 )
|
||||
/// @param szHashValue [out] bSuccess == true 이고... Hash 값 추출을 요청한 경우 추출된 Hash 값 정보를 저장.
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 으로부터 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 Check 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 으로 부터 Data 을 수신하였으나 File Check 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileCheckResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage, unsigned long long& nFileSize, std::string& szHashValue );
|
||||
|
||||
|
||||
/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Unlink 명령 전송.
|
||||
/// @param szFileName [in] 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @param nFileSize [in] 원본 파일의 크기 ( Byte 단위 )
|
||||
/// @param bForceUnlink [in] 강제 삭제 처리 여부 ( Defalult false )
|
||||
/// false : 일반모드 - 해당 파일이 존재하고 Size 값이 동일할 경우에만 삭제처리.. 나머지는 오류로 처리.
|
||||
/// true : 강제모드 - 해당 파일이 존재하지 않거나.. Size 가 틀려도 강제로 삭제 처리.. 오류는 통신, 시스템 오류 발생시에만
|
||||
/// @return Unlink 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileUnlinkRequest(const std::string& szFileName, unsigned long long nFileSize, bool bForceUnlink = false );
|
||||
|
||||
/// @brief File Unlink 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File unlink 처리가 정상적으로 수행되었는지 여부 \n
|
||||
///< ftsd 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 따로 수신하는 Data 는 없음. \n
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 으로부터 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 Unlink 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 으로 부터 Data 을 수신하였으나 File Unlink 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileUnlinkResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage );
|
||||
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460)
|
||||
/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Content Check 명령 전송. (토토디스크 지원용)
|
||||
/// @param szFileName [in] 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @return Check 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileCheckTOTORequest( const std::string& szFileName );
|
||||
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460, #27533)
|
||||
/// @brief File Check 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File Check 처리가 정상적으로 수행되었는지 여부 \n
|
||||
///< ftsd 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 \n
|
||||
///< nFileSize, szHashValue 상에 결과 정보가 저장됨.
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @param nFileSize [out] bSuccess == true 인 경우 요청한 Content 에 대한 file size 정보를 저장. ( Byte 단위 )
|
||||
/// @param szTotoMd5 [out] bSuccess == true 인 경우 추출된 totorasa 용 MD5 Hash 값 정보를 저장.
|
||||
/// @param szChecksum [out] bSuccess == true 인 경우 추출된 totorasa 용 Checksum 정보를 저장.
|
||||
/// @param szDnaCheckKey [out] bSuccess == true 인 경우 추출된 totorasa 용 DNA Check key 값 정보를 저장.
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 으로부터 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 Check 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 으로 부터 Data 을 수신하였으나 File Check 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileCheckTOTOResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage
|
||||
, unsigned long long& nFileSize, std::string& szTotoMd5, std::string& szChecksum, std::string& szDnaCheckKey );
|
||||
|
||||
|
||||
|
||||
/// @brief 연결된 Socket 통신을 이용하여 내부적으로 정의된 Alive Check 패킷 전송 \n
|
||||
///< => 해당 패킷에 대한 처리는 내부적으로 처리되어 결과값을 확인할 필요는 없다.
|
||||
/// @return Socket 연결 해제 또는 전송 관련 오류 발생시 false, 전송 성공시에는 true 반환.
|
||||
bool SendAliveCheck(void);
|
||||
|
||||
/// @brief Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
|
||||
void PrintHeaderToLog(void);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Packet Header 부분의 수신 처리를 위한 함수. Alive Check 요청 패킷은 자동으로 무시처리함.
|
||||
/// @param timeout [in] 대기시간.
|
||||
/// @retrun 성공시 true, 오류 발생및 실패시 fasle 반환.
|
||||
bool 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 );
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __FTSD_SOCKET_CONTROL_H__ */
|
||||
|
||||
@@ -0,0 +1,595 @@
|
||||
#include "BaseSocket.h"
|
||||
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#define SOCKET_NOT_VALID -1
|
||||
#define MAX_SEND_RETRY_COUNT 10 // Socket send 실패시 최대 재전송 시도 횟수.
|
||||
|
||||
|
||||
/// @brief 생성자.
|
||||
/// @param socket [in] 처리할 socket descriptor
|
||||
CBaseSocket::CBaseSocket( const int& socket )
|
||||
: m_sock( socket )
|
||||
{
|
||||
if( m_sock < 0 )
|
||||
{
|
||||
m_sock = SOCKET_NOT_VALID;
|
||||
m_bConnected = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief 소멸자
|
||||
CBaseSocket::~CBaseSocket()
|
||||
{
|
||||
// 소멸자 Socket 명시적 Close 처리.
|
||||
Close();
|
||||
}
|
||||
|
||||
/// @brief 소켓의 Close 처리를 수행함.
|
||||
void CBaseSocket::Close()
|
||||
{
|
||||
m_bConnected = false;
|
||||
|
||||
if( m_sock != SOCKET_NOT_VALID )
|
||||
{
|
||||
close( m_sock );
|
||||
m_sock = SOCKET_NOT_VALID;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief 전달받은 Target 으로 Socket 접속을 수행
|
||||
/// @param szTarget [in] 접속 대상 Host name 또는 IP
|
||||
/// @param nPort [in] 접속 Port
|
||||
/// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
|
||||
bool CBaseSocket::Connect( const std::string& szTarget, int nPort )
|
||||
{
|
||||
// 기존 접속을 Close 처리
|
||||
Close();
|
||||
|
||||
unsigned int nHost = ConversionAddr( szTarget.c_str() );
|
||||
if( nHost == INADDR_NONE )
|
||||
{
|
||||
LOG( LERR, "target host name resolve fail. [%s]->INADDR_NONE", szTarget.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_in stTargetAddr;
|
||||
|
||||
m_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if( m_sock == -1 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
|
||||
LOG( LERR, "socket create failed.[%d][%s] Target:[%s]", errorNum, strerror(errorNum), szTarget.c_str() );
|
||||
|
||||
m_sock = SOCKET_NOT_VALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
stTargetAddr.sin_family = AF_INET;
|
||||
stTargetAddr.sin_addr.s_addr = nHost;
|
||||
stTargetAddr.sin_port = htons( nPort );
|
||||
|
||||
// NEW 2012-05-18 huibong
|
||||
// Connnection 종료시 많은 TIME_WAIT 상태 발생으로 인해 .. 이를 제거하기 위해 SO_LINGER 옵션을 설정처리한다.
|
||||
struct linger opt_linger;
|
||||
opt_linger.l_onoff = 1; /* LINGER ON */
|
||||
|
||||
// CHG 2014-05-27 huibong
|
||||
// 아래와 같이 No Wait 로 처리할 경우... Server 역활 수행 중 Close 신호를 받지 못하는 경우가 발생 가능함.
|
||||
// 따라서 5 sec 정도 대기하도록 수정 처리한다.
|
||||
//opt_linger.l_linger = 0; /* No Wait => 0 for abortive disconnect */
|
||||
opt_linger.l_linger = 5; // 5 sec 대기
|
||||
|
||||
int result = setsockopt( m_sock, SOL_SOCKET, SO_LINGER, &opt_linger, sizeof(opt_linger));
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "setsockopt func SO_LINGER set fail.[%d][%s]", errorNum, strerror(errorNum));
|
||||
|
||||
}
|
||||
|
||||
/* Send Timeout 설정. */
|
||||
struct timeval tv_timeo = { 5, 0 };
|
||||
|
||||
result = setsockopt( m_sock, SOL_SOCKET, SO_SNDTIMEO, &tv_timeo, sizeof(tv_timeo));
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "setsockopt func SO_SNDTIMEO set fail.[%d][%s]", errorNum, strerror(errorNum));
|
||||
|
||||
}
|
||||
|
||||
if(connect(m_sock, (struct sockaddr *)&stTargetAddr, sizeof(stTargetAddr)) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "connect fail.[%s][%d] [%d][%s]", szTarget.c_str(), nPort, errorNum, strerror( errorNum ) );
|
||||
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bConnected = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @brief 멤버 변수인 m_sock 이 유효하고 연결된 상태인 경우 true 반환.
|
||||
/// @return socket이 유효하지 않거나 연결이 끊어지 경우 false 반환, 그외에는 true 반환.
|
||||
bool CBaseSocket::IsValidSocket()
|
||||
{
|
||||
if( m_sock != SOCKET_NOT_VALID && m_bConnected == true )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief m_sock 으로부터 지정된 size 만큼 데이터 read 를 시도 ( read 함수와 동일 )
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t CBaseSocket::Read( void * vptr, size_t size )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
ssize_t nRead = 0;
|
||||
while( (nRead = read( m_sock, vptr, size )) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
|
||||
{
|
||||
nRead = 0;
|
||||
continue;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2010-07-23 BUG huibong 잘못된 대입연산자를 비교연산자로 수정.
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return nRead;
|
||||
}
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t CBaseSocket::ReadN( void * vptr, size_t size )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
ssize_t nRead = 0;
|
||||
size_t nLeft = size;
|
||||
char * ptr = (char *)vptr;
|
||||
|
||||
while( nLeft > 0 )
|
||||
{
|
||||
if( (nRead = read( m_sock, ptr, nLeft )) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
|
||||
{
|
||||
nRead = 0;
|
||||
continue;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nRead == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nLeft -= nRead;
|
||||
ptr += nRead;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
|
||||
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @param timeout [in] Timeout value (sec)
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류 , -2: Timeout
|
||||
ssize_t CBaseSocket::ReadNTimeout( void * vptr, size_t size, int timeout )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
ssize_t nRead = 0;
|
||||
size_t nLeft = size;
|
||||
char * ptr = (char *)vptr;
|
||||
|
||||
struct timeval timeOver;
|
||||
int result;
|
||||
|
||||
fd_set selectFds;
|
||||
FD_ZERO( &selectFds );
|
||||
|
||||
while( nLeft > 0 )
|
||||
{
|
||||
timeOver.tv_sec = timeout;
|
||||
timeOver.tv_usec = 0;
|
||||
FD_SET( m_sock, &selectFds );
|
||||
|
||||
result = select( m_sock+1, &selectFds, (fd_set *)NULL, (fd_set *)NULL, &timeOver );
|
||||
|
||||
if( result > 0 )
|
||||
{
|
||||
if( FD_ISSET( m_sock, &selectFds ))
|
||||
{
|
||||
if( (nRead = read( m_sock, ptr, nLeft) ) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
if(errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK)
|
||||
{
|
||||
nRead = 0;
|
||||
continue;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nRead == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nLeft -= nRead;
|
||||
ptr += nRead;
|
||||
}
|
||||
|
||||
}
|
||||
else if( result == 0 ) // Timeout
|
||||
{
|
||||
//LOG( LDBG, "read timeout");
|
||||
return -2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "select func error.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
|
||||
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
|
||||
///< 본 함수는 Socket 상에 이미 Data 가 존재하는 경우에만 사용.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @param timeout [in] Timeout value (sec)
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
|
||||
ssize_t CBaseSocket::ReadNTimeout2( void * vptr, size_t size, int timeout )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
ssize_t nRead = 0;
|
||||
size_t nLeft = size;
|
||||
char * ptr = (char *)vptr;
|
||||
int errorNum;
|
||||
|
||||
// 우선은 읽기 시도.
|
||||
if( (nRead = read( m_sock, ptr, nLeft) ) < 0 )
|
||||
{
|
||||
errorNum = errno;
|
||||
if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
|
||||
{
|
||||
nRead = 0;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nRead == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nLeft -= nRead;
|
||||
ptr += nRead;
|
||||
|
||||
// 만약 더 받을 데이터가 존재한다면.
|
||||
if( nLeft > 0 )
|
||||
{
|
||||
struct timeval timeOver;
|
||||
int result;
|
||||
|
||||
fd_set selectFds;
|
||||
FD_ZERO( &selectFds );
|
||||
|
||||
while( nLeft > 0 )
|
||||
{
|
||||
timeOver.tv_sec = timeout;
|
||||
timeOver.tv_usec = 0;
|
||||
FD_SET( m_sock, &selectFds );
|
||||
|
||||
result = select( m_sock+1, &selectFds, (fd_set *)NULL, (fd_set *)NULL, &timeOver );
|
||||
|
||||
if( result > 0 )
|
||||
{
|
||||
if( FD_ISSET( m_sock, &selectFds ))
|
||||
{
|
||||
if( (nRead = read( m_sock, ptr, nLeft) ) < 0 )
|
||||
{
|
||||
errorNum = errno;
|
||||
if(errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK)
|
||||
{
|
||||
nRead = 0;
|
||||
continue;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nRead == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nLeft -= nRead;
|
||||
ptr += nRead;
|
||||
}
|
||||
}
|
||||
else if( result == 0 ) // Timeout
|
||||
{
|
||||
//LOG( LDBG, "read timeout");
|
||||
return -2;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorNum = errno;
|
||||
LOG( LERR, "select func error.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// @brief m_sock 으로 지정된 크기만큼 vptr 의 데이터를 전송 시도.\n
|
||||
///< Send Timeout 옵션 설정으로 Write Timeout 설정 가능. \n
|
||||
///< 지정된 횟수만큼 재전송 실패시 오류로 처리함.
|
||||
/// @param vptr [in] 전달할 데이터를 저장한 변수에 대한 포인터.
|
||||
/// @param size [in] write 하고자 하는 데이터의 크기.
|
||||
/// @return write 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t CBaseSocket::WriteN( const void * vptr, size_t size )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
size_t nLeft;
|
||||
ssize_t nWrite;
|
||||
|
||||
const char * ptr = (const char *)vptr;
|
||||
nLeft = size;
|
||||
|
||||
int nTryCount = 0;
|
||||
|
||||
while( nLeft > 0 )
|
||||
{
|
||||
if( (nWrite = send( m_sock, ptr, nLeft, 0 )) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
|
||||
if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
|
||||
{
|
||||
nWrite = 0;
|
||||
++nTryCount;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "send func fail.[%d][%s]", errorNum, strerror(errorNum));
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nWrite == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( nTryCount > MAX_SEND_RETRY_COUNT )
|
||||
{
|
||||
LOG( LERR, "send func failure due to exceeding count of retry[%d/%d]", nTryCount, MAX_SEND_RETRY_COUNT );
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
nLeft -= nWrite;
|
||||
ptr += nWrite;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
// CHG 2011-05-03 huibong
|
||||
// gethostbyname() 함수가 Thread Safe 하지 않기 때문에
|
||||
// DNS resolve 처리시 잘못된 정보를 반환할 가능성이 존재
|
||||
// 이에 따라 본 함수를 수정처리함.
|
||||
/*
|
||||
unsigned int CBaseSocket::ConversionAddr( const char * name )
|
||||
{
|
||||
struct hostent *he;
|
||||
int max;
|
||||
unsigned int retval;
|
||||
|
||||
if ((retval = inet_addr(name)) != INADDR_NONE)
|
||||
return retval;
|
||||
|
||||
he = gethostbyname(name);
|
||||
if (he == NULL)
|
||||
return INADDR_NONE;
|
||||
|
||||
for (max = 0; he->h_addr_list[max]; max++) ;
|
||||
if (max == 1)
|
||||
return *((unsigned int *)(he->h_addr_list[0]));
|
||||
else
|
||||
return *((unsigned int *)(he->h_addr_list[random() % max]));
|
||||
}
|
||||
*/
|
||||
|
||||
unsigned int CBaseSocket::ConversionAddr( const char * name )
|
||||
{
|
||||
unsigned int retval;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *result = NULL;
|
||||
int error;
|
||||
|
||||
|
||||
// 전달받은 정보가 IP 주소인 경우...
|
||||
// - DNS resolve 할 필요 없이 변환시킨 값을 그대로 사용한다.
|
||||
if( (retval = inet_addr(name)) != INADDR_NONE )
|
||||
return retval;
|
||||
|
||||
// getaddrinfo() 함수 호출을 위한 Hint 설정
|
||||
memset( &hints, 0x00, sizeof(hints));
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
// Thread Safe 한 DNS Resolve 처리함수 호출
|
||||
// int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
|
||||
|
||||
// CHG 2014-11-14 huibong
|
||||
// getaddrinfo 호출시 종종 EAI_NONAME 오류가 반환됨. (#20783)
|
||||
// - 따라서 오류 반환시 재시도하도록 기능 추가
|
||||
// CHG 2015-07-15 huibong
|
||||
// usleep 의 multi thread 상에서 block 발생 가능
|
||||
// - nanosleep 을 사용토록 변경 처리
|
||||
for( int count = 0 ; count < 3; count++ )
|
||||
{
|
||||
error = getaddrinfo( name, NULL, &hints, &result );
|
||||
|
||||
// 오류 발생시
|
||||
if( error != 0 )
|
||||
{
|
||||
if( result != NULL)
|
||||
{
|
||||
freeaddrinfo(result);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
// 잠시 대기 후 재시도 처리
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
// 정상 처리된 경우...
|
||||
// - loop 탈출
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 최종 오류 발생시
|
||||
if( error != 0 )
|
||||
{
|
||||
LOG( LERR, "name[%s] dns resolve fail. getaddrinfo return error [%d][%s]", name, error, gai_strerror(error) );
|
||||
return INADDR_NONE;
|
||||
}
|
||||
|
||||
struct sockaddr_in * addr = (struct sockaddr_in *)result->ai_addr;
|
||||
retval = (unsigned int)(addr->sin_addr.s_addr);
|
||||
|
||||
// DNS Resovle 결과 확인용 코드
|
||||
/*
|
||||
struct addrinfo *temp;
|
||||
for( temp = result; temp; temp = temp->ai_next )
|
||||
{
|
||||
addr = (struct sockaddr_in *)temp->ai_addr;
|
||||
printf ("getaddrinfo result = %s\n",inet_ntoa( addr->sin_addr));
|
||||
}
|
||||
*/
|
||||
|
||||
// getaddrinfo() 함수에서 생성한 메모리 영역 해제 처리.
|
||||
freeaddrinfo(result);
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/***************************************************************************
|
||||
BaseSocket ( Base Socket 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 __LIBRARY_BASE_SOCKET_H__
|
||||
#define __LIBRARY_BASE_SOCKET_H__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <string>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#define SOCKET_NOT_VALID -1
|
||||
#define DEFAULT_DATA_RECEIVE_TIMEOUT 15 // sec
|
||||
|
||||
|
||||
class CBaseSocket
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief socket descriptor
|
||||
int m_sock;
|
||||
|
||||
/// @brief socket 의 연결상태인지 여부를 저장하기 위한 변수.
|
||||
bool m_bConnected;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// @brief 생성자.
|
||||
CBaseSocket( const int & socket );
|
||||
|
||||
/// @brief 소멸자.
|
||||
~CBaseSocket();
|
||||
|
||||
/// @brief 소켓의 Close 처리를 수행함.
|
||||
void Close(void);
|
||||
|
||||
/// @brief 전달받은 Target 으로 Socket 접속을 수행
|
||||
/// @param szTarget [in] 접속 대상 Host name 또는 IP
|
||||
/// @param nPort [in] 접속 Port
|
||||
/// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
|
||||
bool Connect( const std::string& szTarget, int nPort );
|
||||
|
||||
/// @brief 멤버 변수인 m_sock 이 유효하고 연결된 상태인 경우 true 반환.
|
||||
/// @return socket이 유효하지 않거나 연결이 끊어지 경우 false 반환, 그외에는 true 반환.
|
||||
bool IsValidSocket(void);
|
||||
|
||||
/// @brief 멤버변수인 socket 의 접속 상태 여부를 설정하기 위한 함수.
|
||||
void SetConnectionStatus( bool bConnected) { m_bConnected = bConnected; }
|
||||
|
||||
/// @brief 현재 socket 의 접속 상태를 반환.
|
||||
bool GetConnectionStatus( void) { return m_bConnected; }
|
||||
|
||||
//void SetLogger( Logger * pLog ) { m_pLog = pLog; }
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief m_sock 로 부터 지정된 size 만큼 데이터 read 를 시도 ( read 함수와 동일 )
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t Read( void * vptr, size_t size );
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t ReadN( void * vptr, size_t size );
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
|
||||
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @param timeout [in] Timeout value (sec)
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
|
||||
ssize_t ReadNTimeout( void * vptr, size_t size, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
|
||||
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
|
||||
///< 본 함수는 Socket 상에 이미 Data 가 존재하는 경우에만 사용.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @param timeout [in] Timeout value (sec)
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
|
||||
ssize_t ReadNTimeout2( void * vptr, size_t size, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
|
||||
|
||||
public:
|
||||
/// @brief m_sock 으로 지정된 크기만큼 vptr 의 데이터를 전송 시도.\n
|
||||
///< Send Timeout 옵션 설정으로 Write Timeout 설정 가능. \n
|
||||
///< 지정된 횟수만큼 재전송 실패시 오류로 처리함.
|
||||
/// @param vptr [in] 전달할 데이터를 저장한 변수에 대한 포인터.
|
||||
/// @param size [in] write 하고자 하는 데이터의 크기.
|
||||
/// @return write 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t WriteN( const void * vptr, size_t size );
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief 문자열로 전달받은 정보를 검사하여 IP 접속 정보를 생성한다.
|
||||
unsigned int ConversionAddr( const char * name );
|
||||
|
||||
};
|
||||
|
||||
#endif /* __LIBRARY_BASE_SOCKET_H__ */
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <boost/algorithm/string.hpp> // use boost
|
||||
|
||||
|
||||
Config::Config()
|
||||
: m_keyDelimiter( CONFIG_DEFAULT_KEY_DELIMITER )
|
||||
, m_valueDelimiter( CONFIG_DEFAULT_VALUE_DELIMITER )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// @brief destructor
|
||||
Config::~Config()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
|
||||
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::Open( const string path )
|
||||
{
|
||||
ifstream file;
|
||||
|
||||
// Config file open
|
||||
file.open( path.c_str());
|
||||
if( file.is_open() == false )
|
||||
{
|
||||
cerr << "[error] " << __FILE__<< ":" << __func__ << ": config file open failed.[" << path << "][" << strerror( errno ) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 기존 데이터가 존재하면 삭제 처리.
|
||||
if( m_configData.empty() == false )
|
||||
m_configData.clear();
|
||||
|
||||
// config file parsing
|
||||
string line;
|
||||
string section;
|
||||
vector< string > configs;
|
||||
|
||||
// 루프를 돌면서 Config 파일을 line 단위로 읽어들인당....
|
||||
while( std::getline( file, line ) )
|
||||
{
|
||||
// 앞뒤 공백 제거 처리
|
||||
boost::trim( line );
|
||||
|
||||
// 공백 or 주석처리 라인 검사
|
||||
if( line.empty() == true || boost::starts_with( line, string("#") ) == true || line == "\r" )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Section 여부 검사
|
||||
if( boost::starts_with( line, string("[") ) == true && boost::ends_with( line, string("]") ) == true )
|
||||
{
|
||||
// [] 로 정의된 section 의 문자열 값 추출
|
||||
line.erase( line.begin() );
|
||||
line.erase( line.end() -1 );
|
||||
boost::trim( line );
|
||||
|
||||
if( section.empty() == false && section != line )
|
||||
{
|
||||
// 이전 세션과 다른 신규 세션인 경우
|
||||
// 기존까지 저장했던 데이터를 멤버 변수에 입력 처리 후 내부변수 초기화 처리.
|
||||
|
||||
m_configData.insert( make_pair( section, configs ) );
|
||||
section.clear();
|
||||
configs.clear();
|
||||
}
|
||||
|
||||
// 신규 section 정보 저장처리.
|
||||
section = line;
|
||||
line.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Section 정보가 아닌 경우 => 실제 설정값 .. ^^
|
||||
configs.push_back( line );
|
||||
}
|
||||
}
|
||||
|
||||
// 마지막 세션 처리된 정보가 존재하는 경우 멤버 변수에 저장 처리.
|
||||
if( section.empty() == false )
|
||||
{
|
||||
m_configData.insert( make_pair( section, configs ) );
|
||||
}
|
||||
|
||||
// 종료 처리.
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
|
||||
/// @return void
|
||||
void Config::Clear()
|
||||
{
|
||||
m_configData.clear();
|
||||
}
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::GetConfig( const string section, const string key, string& value )
|
||||
{
|
||||
// Section 에 대한 임시 데이터 저장객체 생성
|
||||
vector< string > configs;
|
||||
|
||||
// 해당 Section 이 존재하지 않는 경우
|
||||
if( Find( section, configs ) == false )
|
||||
return false;
|
||||
|
||||
string line;
|
||||
string result;
|
||||
vector< string >::const_iterator it;
|
||||
|
||||
// 해당 Key 이 존재하는지 검사
|
||||
for( it = configs.begin(); it != configs.end(); it++)
|
||||
{
|
||||
line = *it;
|
||||
|
||||
// Line 상의 주석 제거
|
||||
string::size_type pos = line.find( '#' );
|
||||
if( pos != string::npos)
|
||||
{
|
||||
line = line.substr(0, pos);
|
||||
boost::trim( line );
|
||||
}
|
||||
|
||||
// key = value 에서 key 부분 추출
|
||||
pos = line.find( m_keyDelimiter );
|
||||
if( pos == string::npos )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = line.substr(0, pos);
|
||||
boost::trim( result );
|
||||
}
|
||||
|
||||
if( key == result )
|
||||
{
|
||||
// Key 값이 동일한 경우
|
||||
value = line.substr( pos+1 );
|
||||
boost::trim( value );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::GetConfig( const string section, const string key, vector< string >& value )
|
||||
{
|
||||
string result;
|
||||
vector< string > vec;
|
||||
|
||||
if( GetConfig( section, key, result ) == false )
|
||||
return false;
|
||||
|
||||
// 전달받은 result 값을 value delimiter 를 이용하여 parsing 처리
|
||||
boost::split( vec, result, boost::is_any_of( m_valueDelimiter ));
|
||||
if( vec.empty() == true )
|
||||
return false;
|
||||
|
||||
// 루프를 돌면서 trim 처리 후 결과값 저장처리.
|
||||
vector< string >::const_iterator it;
|
||||
for( it = vec.begin(); it != vec.end(); it++)
|
||||
{
|
||||
result = boost::trim_copy( *it );
|
||||
|
||||
if( result.empty() == false )
|
||||
value.push_back( result );
|
||||
}
|
||||
|
||||
if( value.empty() == true )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
|
||||
/// @param section [in] section 명
|
||||
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::Find( const string& section, vector<string>& configData )
|
||||
{
|
||||
map< string, vector< string > >::iterator it = m_configData.find( section );
|
||||
if( it != m_configData.end() )
|
||||
{
|
||||
// 해당 section 을 찾은 경우
|
||||
configData = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 해당 section 을 찾지 못한 경우
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/***************************************************************************
|
||||
Config File Parser Class
|
||||
-----------------------------------------
|
||||
begin : 2010/02/27
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of SolutionBox Inc.
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef __LIBRARY_CONFIG_H__
|
||||
#define __LIBRARY_CONFIG_H__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#define CONFIG_DEFAULT_KEY_DELIMITER "="
|
||||
#define CONFIG_DEFAULT_VALUE_DELIMITER ","
|
||||
|
||||
|
||||
/// @brief Config class
|
||||
/// 1. config 파일의 내용을 파싱처리하여 각 Section, Key 에 해당하는 값을 반환처리
|
||||
/// 2. config 파일은 [section] 단위로 구분된다.
|
||||
/// 3. config 파일은 key=value 로 구분가능하며 이는 Delimiter 설정을 통해 변경가능하다.
|
||||
/// 4. value 값이 다중으로 존재하는 경우 "," 값을 통해 구분가능, Delimiter 변경시 다른 값도 사용가능함.
|
||||
/// 5. config 파일상에서 "#" 로 시작하는 문자열은 라인 끝까지 주석으로 처리된다.
|
||||
/// 6. 특정 Key 값에 대한 다중 value 값 조회는 vector<string> 을 통해 수행한다.
|
||||
/// 7. 만약 다중 value 값이 존재시 string 으로 반환받을 경우 해당 Row 가 통째로 반환된다.
|
||||
/// 8. 본 객체는 Open , Clear 함수가 호출되기전까지 이전 Config 정보가 저장된다.
|
||||
class Config
|
||||
{
|
||||
private:
|
||||
/// @brief config information saved variable
|
||||
// string : section data
|
||||
// vector<string> : key=value data
|
||||
map< string, vector< string> > m_configData;
|
||||
|
||||
/// @brief Key, Value 구분자 저장 변수
|
||||
string m_keyDelimiter;
|
||||
string m_valueDelimiter;
|
||||
|
||||
public:
|
||||
/// @brief constructor
|
||||
Config();
|
||||
|
||||
/// @brief destructor
|
||||
~Config();
|
||||
|
||||
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
|
||||
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Open( const string path );
|
||||
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
|
||||
/// @return void
|
||||
void Clear();
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
|
||||
/// @return On success return true, otherwise return false
|
||||
bool GetConfig( const string section, const string key, string& value );
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool GetConfig( const string section, const string key, vector< string >& value );
|
||||
|
||||
/// @brief Key, Value Delimiter Get 함수
|
||||
/// @param key [out] key delimiter 값
|
||||
/// @param value [out] value delimiter 값
|
||||
void GetDelimiter( string& key, string& value )
|
||||
{
|
||||
key = m_keyDelimiter;
|
||||
value = m_valueDelimiter;
|
||||
}
|
||||
|
||||
// BUG 2010-12-06 huibong
|
||||
// string& value = CONFIG_DEFAULT_VALUE_DELIMITER 값은 문법상 오류 구문임.
|
||||
// gcc 3.4.6 버전에서는 Compile 되나 gcc 4.4.5 에서는 error 로 처리되어 수정처리함.
|
||||
|
||||
/// @brief Key, Value Delimiter Set 함수
|
||||
/// @param key [out] key delimiter 값
|
||||
/// @param value [out] value delimiter 값, 지정하지 않을 경우 Default 값이 사용됨.
|
||||
void SetDelimiter( string& key, string value = CONFIG_DEFAULT_VALUE_DELIMITER )
|
||||
{
|
||||
m_keyDelimiter = key;
|
||||
m_valueDelimiter = value;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
|
||||
/// @param section [in] section 명
|
||||
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Find( const string& section, vector<string>& configData );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* __LIBRARY_CONFIG_H__ */
|
||||
@@ -0,0 +1,487 @@
|
||||
/***************************************************************************
|
||||
Logger.cpp
|
||||
-----------------------------------------
|
||||
begin : 2011/10/26
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of SolutionBox Inc.
|
||||
***************************************************************************/
|
||||
#include "Logger.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <iostream>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_BUFFER_SIZE 2048 // 임시 버퍼 최대 크기
|
||||
#define DEFAULT_LOG_LEVEL LINF
|
||||
|
||||
using namespace std;
|
||||
|
||||
CLogger* CLogger::m_pInstance = NULL;
|
||||
bool CLogger::m_bIsInitialized = false;
|
||||
pthread_mutex_t CLogger::m_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
|
||||
CLogger::CLogger( string programName, string logDir, int logLevel )
|
||||
{
|
||||
m_szProgramName = programName;
|
||||
m_szLogDir = logDir;
|
||||
|
||||
if( IsValidLogLevel( logLevel ) == true )
|
||||
{
|
||||
m_nLogLevel = logLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nLogLevel = DEFAULT_LOG_LEVEL;
|
||||
}
|
||||
|
||||
MakeLogLevelString();
|
||||
}
|
||||
|
||||
void CLogger::MakeLogLevelString()
|
||||
{
|
||||
m_vectorLogLevelString.push_back( "EMR" );
|
||||
m_vectorLogLevelString.push_back( "ALT" );
|
||||
m_vectorLogLevelString.push_back( "CRT" );
|
||||
m_vectorLogLevelString.push_back( "ERR" );
|
||||
m_vectorLogLevelString.push_back( "WAR" );
|
||||
m_vectorLogLevelString.push_back( "NOT" );
|
||||
m_vectorLogLevelString.push_back( "INF" );
|
||||
m_vectorLogLevelString.push_back( "DBG" );
|
||||
m_vectorLogLevelString.push_back( "DEV" );
|
||||
m_vectorLogLevelString.push_back( "DEV1" );
|
||||
m_vectorLogLevelString.push_back( "DEV2" );
|
||||
}
|
||||
|
||||
CLogger::~CLogger()
|
||||
{
|
||||
m_vectorLogLevelString.clear();
|
||||
}
|
||||
|
||||
bool CLogger::Init( string programName, string logDir, int logLevel )
|
||||
{
|
||||
if( m_bIsInitialized == true )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
pthread_mutex_lock( &m_mutex );
|
||||
|
||||
// 변수 유효성 검사.
|
||||
if( programName.empty() == true || logDir.empty() == true )
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( MakeLogDir( programName, logDir ) == false )
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( CLogger::m_pInstance != NULL )
|
||||
{
|
||||
delete CLogger::m_pInstance;
|
||||
CLogger::m_pInstance = NULL;
|
||||
}
|
||||
|
||||
CLogger::m_pInstance = new CLogger( programName, logDir, logLevel );
|
||||
m_bIsInitialized = true;
|
||||
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::IsValidLogLevel( int logLevel )
|
||||
{
|
||||
return ( ( logLevel < 0 || logLevel > MAX_LOG_LEVEL ) ? false : true );
|
||||
}
|
||||
|
||||
bool CLogger::SetLogLevel( int logLevel )
|
||||
{
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_nLogLevel = logLevel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::MakeLogDir( string programName, string logDir )
|
||||
{
|
||||
string path = logDir + "/" + programName;
|
||||
|
||||
struct stat dirStat;
|
||||
|
||||
// 해당 이름을 가진 파일 또는 디렉토리가 존재하고
|
||||
if( lstat( path.c_str(), &dirStat ) == 0 )
|
||||
{
|
||||
// 해당 이름이 디렉토리인 경우
|
||||
if( S_ISDIR( dirStat.st_mode ) == true )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "exist file with the same name as log path(" << path << ")." << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 디렉토리 가 존재하지 않는 경우 디렉토리 생성 시도
|
||||
string cmd = "mkdir -p " + path;
|
||||
system( cmd.c_str() );
|
||||
|
||||
if( IsDirectory( path ) == false )
|
||||
{
|
||||
cerr << "can't make log directory. path=" << path << "." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::IsDirectory( string path )
|
||||
{
|
||||
struct stat dirStat;
|
||||
|
||||
if( lstat ( path.c_str(), &dirStat ) != 0 )
|
||||
{
|
||||
cerr << "Log path not valid. Check path [" << path << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 해당 정보가 Directory 가 아닌 경우
|
||||
if( S_ISDIR( dirStat.st_mode ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CLogger::Exit()
|
||||
{
|
||||
if( m_bIsInitialized == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock( &m_mutex );
|
||||
|
||||
if( CLogger::m_pInstance != NULL )
|
||||
{
|
||||
delete CLogger::m_pInstance;
|
||||
CLogger::m_pInstance = NULL;
|
||||
m_bIsInitialized = false;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
}
|
||||
|
||||
CLogger* CLogger::GetInstance()
|
||||
{
|
||||
return CLogger::m_pInstance;
|
||||
}
|
||||
|
||||
string CLogger::GetLogFilename( struct tm &timeNow )
|
||||
{
|
||||
char timeStr[256];
|
||||
snprintf( timeStr, (size_t)256, "%04d%02d%02d.log", timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday);
|
||||
|
||||
// Make File Name
|
||||
string filename = m_szLogDir + "/" + m_szProgramName + "/" + m_szProgramName + "_" + string( timeStr );
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
bool CLogger::Write( int logLevel, const char * fmt, ...)
|
||||
{
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return Write( logLevel, PREFIX_DATE, NULL, 0, buffer );
|
||||
}
|
||||
|
||||
bool CLogger::WriteNoPrefix( int logLevel, const char * fmt, ...)
|
||||
{
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return Write( logLevel, PREFIX_NONE, NULL, 0, buffer );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// CHG 2012-08-16 huibong
|
||||
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
|
||||
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
|
||||
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
|
||||
// 함수명 : Write -> WriteWithFunc 으로 변경 처리
|
||||
bool CLogger::WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
|
||||
{
|
||||
if( filename == NULL || funcname == NULL || lineNum < 0 || fmt == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
|
||||
char className[255];
|
||||
string funcPrefix = GetClassName( className, filename );
|
||||
funcPrefix += "::" + string( funcname ) + "()";
|
||||
|
||||
return Write( logLevel, PREFIX_FUNCTION, funcPrefix.c_str(), lineNum, buffer );
|
||||
}
|
||||
|
||||
const char* CLogger::GetClassName( char* className, const char* filename )
|
||||
{
|
||||
if( className == NULL )
|
||||
{
|
||||
cout << "[ERROR] The input argument 'className' is NULL." << endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( filename == NULL )
|
||||
{
|
||||
cout << "[ERROR] The input argument 'filename' is NULL." << endl;
|
||||
strcpy( className, "NULL" );
|
||||
return className;
|
||||
}
|
||||
|
||||
int filenameSize = strlen( filename );
|
||||
memcpy( className, filename, filenameSize );
|
||||
|
||||
// find end position
|
||||
char endCharacter = '.';
|
||||
int endPos = filenameSize - 1;
|
||||
for( ; endPos > 0; --endPos )
|
||||
{
|
||||
if( className[endPos] == endCharacter )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( endPos == 0 )
|
||||
{
|
||||
cout << "[ERROR] endPos=0" << endl;
|
||||
strcpy( className, "NULL" );
|
||||
return className;
|
||||
}
|
||||
|
||||
className[endPos] = '\0';
|
||||
|
||||
// find start position
|
||||
char startCharacter = '/';
|
||||
int startPos = endPos - 1;
|
||||
for( ; startPos > 0; --startPos )
|
||||
{
|
||||
if( className[startPos] == startCharacter )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( startPos != 0 )
|
||||
{
|
||||
startPos += 1;
|
||||
}
|
||||
|
||||
return (className + startPos );
|
||||
}
|
||||
|
||||
bool CLogger::Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log )
|
||||
{
|
||||
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
|
||||
if( logLevel > m_nLogLevel )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( log == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Current Data & Time
|
||||
time_t now = time( NULL );
|
||||
struct tm timeNow;
|
||||
localtime_r( &now, &timeNow );
|
||||
|
||||
string filename = GetLogFilename( timeNow );
|
||||
|
||||
// Log file open
|
||||
FILE* pFile = NULL;
|
||||
pFile = fopen( filename.c_str(), "a+" );
|
||||
|
||||
if( pFile == NULL )
|
||||
{
|
||||
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 시간 정보처리
|
||||
char timeStr[256];
|
||||
snprintf( timeStr, (size_t)256, "[%02d:%02d:%02d]"
|
||||
, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec );
|
||||
|
||||
// Log Level 문자열 검색
|
||||
string szLevel = m_vectorLogLevelString[ logLevel ];
|
||||
|
||||
switch( logPrefix )
|
||||
{
|
||||
case PREFIX_DATE:
|
||||
/// 형식 예: [15:47:41] [DBG] sample log message.
|
||||
fprintf( pFile, "%s [%-4s] %s\n", timeStr, szLevel.c_str(), log );
|
||||
break;
|
||||
|
||||
case PREFIX_FUNCTION:
|
||||
if( functionName == NULL )
|
||||
{
|
||||
fclose( pFile );
|
||||
return false;
|
||||
}
|
||||
/// 형식 예: [15:47:41] [DBG] sample log message. [SomeClass::SomeFunction():12]
|
||||
fprintf( pFile, "%s [%-4s] %s [%s:%d]\n", timeStr, szLevel.c_str(), log, functionName, lineNum );
|
||||
break;
|
||||
|
||||
case PREFIX_NONE:
|
||||
/// 형식 예: sample log message.
|
||||
fprintf( pFile, "%s\n", log );
|
||||
break;
|
||||
}
|
||||
// Write to log file
|
||||
fflush( pFile );
|
||||
|
||||
// 종료 처리.
|
||||
fclose( pFile );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::WriteHex( int logLevel, const unsigned char* data, const int size )
|
||||
{
|
||||
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
|
||||
if( logLevel > m_nLogLevel )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( data == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( size <= 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Current Data & Time
|
||||
time_t now = time( NULL );
|
||||
struct tm timeNow;
|
||||
localtime_r( &now, &timeNow );
|
||||
|
||||
string filename = GetLogFilename( timeNow );
|
||||
|
||||
// Log file open
|
||||
FILE* pFile = NULL;
|
||||
pFile = fopen( filename.c_str(), "a+" );
|
||||
|
||||
if( pFile == NULL )
|
||||
{
|
||||
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for( int i = 0; i < size; ++i )
|
||||
{
|
||||
fprintf( pFile, "%02X", data[i] );
|
||||
|
||||
if( i == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ( (i+1) % 8 ) == 0 )
|
||||
{
|
||||
fprintf( pFile, " " );
|
||||
}
|
||||
|
||||
if( ( (i+1) % 16 ) == 0 )
|
||||
{
|
||||
fprintf( pFile, " " );
|
||||
}
|
||||
|
||||
if( ( (i+1) % 32 ) == 0 )
|
||||
{
|
||||
fprintf( pFile, "\n" );
|
||||
}
|
||||
}
|
||||
fprintf( pFile, "\n" );
|
||||
fflush( pFile );
|
||||
|
||||
// 종료 처리.
|
||||
fclose( pFile );
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/***************************************************************************
|
||||
Logger.h
|
||||
-----------------------------------------
|
||||
begin : 2011/10/26
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 3.2.0.805
|
||||
|
||||
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of SolutionBox Inc.
|
||||
***************************************************************************/
|
||||
#ifndef __LOGGER_H__
|
||||
#define __LOGGER_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <pthread.h>
|
||||
|
||||
///@brief 로그 레벨 정의
|
||||
#define LEMR 0 /* system is or will be unusable if situation is not resolved */
|
||||
#define LALT 1 /* immediate action required */
|
||||
#define LCRT 2 /* critical situations */
|
||||
#define LERR 3 /* error conditions */
|
||||
#define LWAR 4 /* recoverable errors */
|
||||
#define LNOT 5 /* unusual situation that merits investigation */
|
||||
#define LINF 6 /* information messages */
|
||||
#define LDBG 7 /* verbose data for debugging */
|
||||
#define LDEV 8 /* verbose data for developer */
|
||||
#define LDEV1 9 /* start or end the function of application level */
|
||||
#define LDEV2 10 /* start or end the function of application level */
|
||||
|
||||
#define MAX_LOG_LEVEL LDEV2
|
||||
|
||||
|
||||
///@brief Log 정보를 파일로 저장하기 위한 Class.
|
||||
///1. 가변 format 으로 전달된 로그 관련 정보를 Log Level 에 따라 로그 파일에 아래의 4가지 형식으로 저장한다.
|
||||
///
|
||||
/// 1.1 Prefix로 [hh:mm:ss]와 [ClassName::FunctionName]이 추가된 로그
|
||||
/// LOG( level, format, ... ) 매크로 사용.
|
||||
/// 예) [17:43:40] [DBG] log level debug [LoggerTestTestLogger]
|
||||
///
|
||||
/// 1.2 Prefix로 [hh:mm:ss]이 추가된 로그
|
||||
/// _LOG( level, format, ... ) 매크로 사용
|
||||
/// 예) [17:43:40] [EMR] log level emergency
|
||||
///
|
||||
/// 1.3 Prefix가 없는 로그
|
||||
/// _LOG_( level, format, ... ) 매크로 사용
|
||||
/// 예) log level emergency
|
||||
///
|
||||
/// 1.4 Hex 로그.
|
||||
/// LOG_HEX( level, data, size ) 매크로 사용
|
||||
/// 예) 00010203 04050607 08090A0B 0C0D0E0F
|
||||
///
|
||||
///2. Logger 객체 초기화시 전달된 Log Level 정보보다 전달받은 Log Level 정보가 큰 경우 해당 로그는 파일로 저장되지 않는다.
|
||||
///
|
||||
///3. 로그 파일은 매 일단위로 저장파일이 변경된다.
|
||||
///
|
||||
///4. 로그 저장을 위한 program 경로가 존재하지 않는 경우 자동 생성 처리된다.
|
||||
///
|
||||
///5. 로그 저장방식은 매 저장로그마다 open-close 로 처리된다.
|
||||
///
|
||||
///6. 파일로 기록시 Log Level 에 대한 정보도 함께 기록된다.
|
||||
///
|
||||
///7. 싱글톤으로 작성되었고, CLogger::Init(...)시에 쓰레드 안정성을 제공한다.
|
||||
///
|
||||
///8. 동적으로 로그 레벨을 변경할 수 있는 인터페이스를 제공한다.
|
||||
///
|
||||
class CLogger
|
||||
{
|
||||
// Attributes
|
||||
private:
|
||||
///@brief 싱글톤 객체 인트턴스.
|
||||
static CLogger* m_pInstance;
|
||||
|
||||
///@brief 싱글톤 객체 초기화 여부.
|
||||
static bool m_bIsInitialized;
|
||||
|
||||
///@brief 싱글톤 객체 초기화시 스레드 안정성을 위한 뮤텍스.
|
||||
static pthread_mutex_t m_mutex;
|
||||
|
||||
///@brief 프로그램 이름. 로그 파일 경로를 만들 때 사용.
|
||||
std::string m_szProgramName;
|
||||
|
||||
///@brief 공통 로그 디렉토리 이름. 로그 파일 경로를 만들 때 사용.
|
||||
std::string m_szLogDir;
|
||||
|
||||
///@brief 로그 레벨.
|
||||
int m_nLogLevel;
|
||||
|
||||
///@brief 로그 레벨에 대응되는 문자열 정보를 저장.
|
||||
std::vector<std::string> m_vectorLogLevelString;
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
///@brief 로그 형식을 지정.
|
||||
typedef enum
|
||||
{
|
||||
PREFIX_NONE = 0, /// 클래스 설명의 1.3에 해당
|
||||
PREFIX_DATE, /// 클래스 설명의 1.2에 해당
|
||||
PREFIX_FUNCTION, /// 클래스 설명의 1.1에 해당
|
||||
} LOG_PREFIX;
|
||||
|
||||
// Operations
|
||||
private:
|
||||
///@brief 생성자.
|
||||
/// 프로그램 이름, 공통 로그 디렉토리, 로그 레벨을 저장하고
|
||||
/// 로그 레벨에 대응되는 문자열 정보를 만든다.
|
||||
///@param programName [in] 프로그램 이름
|
||||
///@param logDir [in] 공통 로그 디렉토리 경로
|
||||
///@param logLevel [in] 로그 레벨
|
||||
CLogger( std::string programName, std::string logDir, int logLevel );
|
||||
|
||||
///@brief 소멸자.
|
||||
/// 로그 레벨에 대응되는 문자열 정보를 저장하고 있는
|
||||
/// 벡터 m_vectorLogLevelString을 clear 시킴.
|
||||
virtual ~CLogger();
|
||||
|
||||
///@brief 로그 레벨에 대응되는 문자열을 만든다.
|
||||
///@param none.
|
||||
///@return none.
|
||||
void MakeLogLevelString();
|
||||
|
||||
///@brief 로그 파일이 위치할 실제 로그 디렉토리를 생성한다.
|
||||
/// 생성할 디렉토리 경로는 'logDir/programName'이 된다.
|
||||
///@param programName [in] 프로그램 이름
|
||||
///@param logDir [in] 공통 로그 디렉토리 경로
|
||||
///@return 디렉토리가 이미 존재하거나 생성 성공하면 true,
|
||||
/// 해당 경로가 존재하지만 디렉토리가 아니거나, 디렉토리 생성 실패하면 false 반환.
|
||||
static bool MakeLogDir( std::string programName, std::string logDir );
|
||||
|
||||
///@brief 해당 경로가 디렉토리 인지 아닌지 판단.
|
||||
///@param path [in] 디렉토리 인지 아닌지 판단할 경로.
|
||||
///@return 해당 경로가 디렉토이면 true,
|
||||
/// 경로가 존재하지 않거나 디렉토리가 아니면 false 반환.
|
||||
static bool IsDirectory( std::string path );
|
||||
|
||||
///@brief 로그 레벨이 올바른지 판별.
|
||||
///@param logLevel [in]
|
||||
///@return 올바른 로그 레벨이면 true, 그렇지 않으면 false 반환.
|
||||
static bool IsValidLogLevel( int logLevel );
|
||||
|
||||
///@brief 시간 정보를 입력 받아 로그 파일 이름을 만든다.
|
||||
/// 로그 파일 이름 형식 : 프로그램명_YYYYMMDD.log
|
||||
///param timeNow [in] 현재 시간 정보.
|
||||
///return 로그 파일 이름.
|
||||
std::string GetLogFilename( struct tm &timeNow );
|
||||
|
||||
///@brief 로그를 남기는는 클래스가 정의된 파일의 이름에서 클래스명을 추출한다.
|
||||
/// 쓰레드 안정성을 보장한다.
|
||||
///@param className [out] 파일 이름에서 추출된 클래스명
|
||||
///@param filename [in] 파일 이름.
|
||||
///@return 클래스명 추출이 성공하면 클래스명 문자열의 포인터, 추출 실패하면 NULL.
|
||||
const char* GetClassName( char* className, const char* filename );
|
||||
|
||||
///@brief 인자 logPrefix에 따라 적절한 형식으로 로그 파일에 로그를 저장한다.
|
||||
/// 인자 logLevel이 설정된 로그 레벨보다 높으면 로그를 출력하지 않는다.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param logPrefix [in] 로그 프리픽스 종류.
|
||||
///@functionName [in] ClassName::FunctionNmae() 형식의 문자열.
|
||||
///@lineNum [in] 라인 번호.
|
||||
///@log [in] 출력하고자 하는 로그 내용.
|
||||
bool Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log );
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
///@brief 싱글톤 객체 m_pInstance를 생성하고 인자 정보로 로그 디렉토리를 만든다.
|
||||
///@param programName [in] 프로그램 이름
|
||||
///@param logDir [in] 공통 로그 디렉토리 경로
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@return 로그 디렉토리를 만들고 싱글톤 객체를 생성했으면 true,
|
||||
/// 로그 디렉토리를 만들지 못 했거나 인자 값이 올바르지 않으면 false 반환.
|
||||
static bool Init( std::string programName, std::string logDir, int logLevel );
|
||||
|
||||
///@brief 싱글톤 객체 m_pInstance를 delete 한다.
|
||||
///@param none.
|
||||
///@return none.
|
||||
static void Exit();
|
||||
|
||||
///@brief 싱글톤 객체 m_pInstance를 반환한다.
|
||||
///@param none.
|
||||
///@return CLogger 객체의 인스턴스.
|
||||
static CLogger* GetInstance();
|
||||
|
||||
///@brief Prefix로 [hh:mm:ss]이 추가된 형식으로 로그 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param fmt [in] 로그 내용 포맷.
|
||||
///@param __VAR_ARGS__ [in] 가변 인자.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool Write( int logLevel, const char * fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
///@brief Prefix가 없는 로그 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param fmt [in] 로그 내용 포맷.
|
||||
///@param __VAR_ARGS__ [in] 가변 인자.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool WriteNoPrefix( int logLevel, const char * fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
|
||||
// CHG 2012-08-16 huibong
|
||||
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
|
||||
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
|
||||
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
|
||||
|
||||
///@brief Prefix로 [hh:mm:ss]와 [ClassName::FunctionName:line]이 추가된 형식으로 로그 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param filename [in] 파일 이름
|
||||
///@param funcname [in] 함수 이름
|
||||
///@param lineNum [in] 라인 번호
|
||||
///@param fmt [in] 로그 내용 포맷.
|
||||
///@param __VAR_ARGS__ [in] 가변 인자.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
|
||||
__attribute__((format(printf, 6, 7)));
|
||||
|
||||
///@brief 로그를 hex 형식으로 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param data [in] hex 형식으로 출력할 데이터.
|
||||
///@pram size [in] data의 크기.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool WriteHex( int logLevel, const unsigned char* data, const int size );
|
||||
|
||||
///@brief 로그을 설정한다. 인자 logLevel이 적절한 값이면 새로운 값으로 변경하고
|
||||
/// 적절한 값이 아니면 로그 레벨을 변경하지 않는다.
|
||||
///@param logLevel [in] 설정할 로그 레벨
|
||||
///@return none.
|
||||
bool SetLogLevel( int logLevel );
|
||||
|
||||
inline int GetLogLevel() { return m_nLogLevel; };
|
||||
inline std::string GetLogDir() { return m_szLogDir + "/" + m_szProgramName; };
|
||||
};
|
||||
|
||||
#define LOG( level, format, ... ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->WriteWithFunc( level, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
|
||||
#define _LOG( level, format, ... ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->Write( level, format, ##__VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
|
||||
#define _LOG_( level, format, ... ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->WriteNoPrefix( level, format, ##__VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
|
||||
#define _LOG_HEX_( level, data, size ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->WriteHex( level, (const unsigned char*)data, size ); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define FUNC_BEGIN() LOG( LDEV1, "begin" )
|
||||
#define FUNC_END() LOG( LDEV1, "end" )
|
||||
|
||||
#define FRM_BEGIN() LOG( LDEV2, "begin" )
|
||||
#define FRM_END() LOG( LDEV2, "end" )
|
||||
|
||||
#endif // __LOGGER_H__
|
||||
@@ -0,0 +1,69 @@
|
||||
#****************************************************************************
|
||||
# Makefile for Cloud Storage Common Libaray
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2013/06/04
|
||||
# copyright : (C) 2013 Solbox Inc.
|
||||
# author : Development 1 Team
|
||||
# email : dev1@solbox.com
|
||||
# version : 3.3.0
|
||||
#
|
||||
# CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
# Redistribution and use in source and binary forms, with or with out
|
||||
# modification, are not permitted in outside of Solbox Inc.
|
||||
#*****************************************************************************
|
||||
|
||||
# Library info
|
||||
|
||||
LIB_NAME = InterCommon
|
||||
|
||||
LIB = lib$(LIB_NAME).a
|
||||
|
||||
OBJS = Config.o Logger.o BaseSocket.o ProcessRename.o ReportLog.o
|
||||
|
||||
# Compiler info
|
||||
CC = /usr/bin/g++
|
||||
AR = /usr/bin/ar
|
||||
|
||||
DIR_INCLUDE = -I/usr/local/include
|
||||
|
||||
ifeq ($(DEBUG), yes)
|
||||
CFLAGS = -Wall -O0 -g -Wreturn-type -Wunused -Wuninitialized\
|
||||
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
|
||||
-D_REENTRANT -D_THREAD_SAFE -D_PTHREADS
|
||||
LFLAGS =
|
||||
DFLAGS =
|
||||
else
|
||||
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized\
|
||||
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
|
||||
-D_REENTRANT -D_THREAD_SAFE -D_PTHREADS
|
||||
LFLAGS =
|
||||
DFLAGS =
|
||||
endif
|
||||
############################
|
||||
|
||||
all:$(LIB)
|
||||
sync
|
||||
|
||||
%.o: %.cpp
|
||||
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
|
||||
|
||||
|
||||
$(LIB): $(OBJS)
|
||||
$(AR) crsv $@ $^
|
||||
|
||||
|
||||
clean:
|
||||
-rm -f *.o core *.out *.log
|
||||
-rm -f $(LIB)
|
||||
sync
|
||||
|
||||
|
||||
install :
|
||||
sync
|
||||
|
||||
|
||||
# End of Makefile
|
||||
@@ -0,0 +1,185 @@
|
||||
#include "ProcessRename.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
typedef size_t Size;
|
||||
#define LONG_ALIGN_MASK (sizeof(long) - 1)
|
||||
#define MEMSET_LOOP_LIMIT 1024
|
||||
#define MemSet(start, val, len) \
|
||||
do \
|
||||
{ \
|
||||
/* must be void* because we don't know if it is integer aligned yet */ \
|
||||
void *_vstart = (void *) (start); \
|
||||
int _val = (val); \
|
||||
Size _len = (len); \
|
||||
\
|
||||
if ((((long) _vstart) & LONG_ALIGN_MASK) == 0 && \
|
||||
(_len & LONG_ALIGN_MASK) == 0 && \
|
||||
_val == 0 && \
|
||||
_len <= MEMSET_LOOP_LIMIT && \
|
||||
/* \
|
||||
* If MEMSET_LOOP_LIMIT == 0, optimizer should find \
|
||||
* the whole "if" false at compile time. \
|
||||
*/ \
|
||||
MEMSET_LOOP_LIMIT != 0) \
|
||||
{ \
|
||||
long *_start = (long *) _vstart; \
|
||||
long *_stop = (long *) ((char *) _start + _len); \
|
||||
while (_start < _stop) \
|
||||
*_start++ = 0; \
|
||||
} \
|
||||
else \
|
||||
memset(_vstart, _val, _len); \
|
||||
} while (0)
|
||||
|
||||
#define PS_PADDING '\0'
|
||||
|
||||
extern char **environ;
|
||||
bool update_process_title = true;
|
||||
|
||||
static char *ps_buffer; /* will point to argv area */
|
||||
static size_t ps_buffer_size; /* space determined at run time */
|
||||
static size_t last_status_len; /* use to minimize length of clobber */
|
||||
static size_t ps_buffer_cur_len; /* nominal strlen(ps_buffer) */
|
||||
static size_t ps_buffer_fixed_size; /* size of the constant prefix */
|
||||
|
||||
static int save_argc;
|
||||
static char **save_argv;
|
||||
|
||||
size_t strlcpy(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
|
||||
/* Copy as many bytes as will fit */
|
||||
if (n != 0)
|
||||
{
|
||||
while (--n != 0)
|
||||
{
|
||||
if ((*d++ = *s++) == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||
if (n == 0)
|
||||
{
|
||||
if (siz != 0)
|
||||
*d = '\0'; /* NUL-terminate dst */
|
||||
while (*s++)
|
||||
;
|
||||
}
|
||||
|
||||
return (s - src - 1); /* count does not include NUL */
|
||||
}
|
||||
|
||||
|
||||
void set_ps_display(const char *activity, bool force)
|
||||
{
|
||||
/* update_process_title=off disables updates, unless force = true */
|
||||
if (!force && !update_process_title)
|
||||
return;
|
||||
|
||||
/* no ps display for stand-alone backend */
|
||||
//if (!IsUnderPostmaster)
|
||||
// return;
|
||||
|
||||
/* If ps_buffer is a pointer, it might still be null */
|
||||
if (!ps_buffer)
|
||||
return;
|
||||
/* Update ps_buffer to contain both fixed part and activity */
|
||||
strlcpy(ps_buffer + ps_buffer_fixed_size, activity,
|
||||
ps_buffer_size - ps_buffer_fixed_size);
|
||||
ps_buffer_cur_len = strlen(ps_buffer);
|
||||
|
||||
/* pad unused memory; need only clobber remainder of old status string */
|
||||
if (last_status_len > ps_buffer_cur_len)
|
||||
MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING,
|
||||
last_status_len - ps_buffer_cur_len);
|
||||
last_status_len = ps_buffer_cur_len;
|
||||
|
||||
}
|
||||
|
||||
|
||||
char ** save_ps_display_args(int argc, char **argv)
|
||||
{
|
||||
save_argc = argc;
|
||||
save_argv = argv;
|
||||
|
||||
/*
|
||||
* If we're going to overwrite the argv area, count the available space.
|
||||
* Also move the environment to make additional room.
|
||||
*/
|
||||
{
|
||||
char *end_of_area = NULL;
|
||||
char **new_environ;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* check for contiguous argv strings
|
||||
*/
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
if (i == 0 || end_of_area + 1 == argv[i])
|
||||
end_of_area = argv[i] + strlen(argv[i]);
|
||||
}
|
||||
|
||||
if (end_of_area == NULL) /* probably can't happen? */
|
||||
{
|
||||
ps_buffer = NULL;
|
||||
ps_buffer_size = 0;
|
||||
return argv;
|
||||
}
|
||||
|
||||
/*
|
||||
* check for contiguous environ strings following argv
|
||||
*/
|
||||
for (i = 0; environ[i] != NULL; i++)
|
||||
{
|
||||
if (end_of_area + 1 == environ[i])
|
||||
end_of_area = environ[i] + strlen(environ[i]);
|
||||
}
|
||||
|
||||
ps_buffer = argv[0];
|
||||
last_status_len = ps_buffer_size = end_of_area - argv[0];
|
||||
|
||||
/*
|
||||
* move the environment out of the way
|
||||
*/
|
||||
new_environ = (char **) malloc((i + 1) * sizeof(char *));
|
||||
for (i = 0; environ[i] != NULL; i++)
|
||||
new_environ[i] = strdup(environ[i]);
|
||||
new_environ[i] = NULL;
|
||||
environ = new_environ;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're going to change the original argv[] then make a copy for
|
||||
* argument parsing purposes.
|
||||
*
|
||||
* (NB: do NOT think to remove the copying of argv[], even though
|
||||
* postmaster.c finishes looking at argv[] long before we ever consider
|
||||
* changing the ps display. On some platforms, getopt() keeps pointers
|
||||
* into the argv array, and will get horribly confused when it is
|
||||
* re-called to analyze a subprocess' argument string if the argv storage
|
||||
* has been clobbered meanwhile. Other platforms have other dependencies
|
||||
* on argv[].
|
||||
*/
|
||||
{
|
||||
char **new_argv;
|
||||
int i;
|
||||
|
||||
new_argv = (char **) malloc((argc + 1) * sizeof(char *));
|
||||
for (i = 0; i < argc; i++)
|
||||
new_argv[i] = strdup(argv[i]);
|
||||
new_argv[argc] = NULL;
|
||||
|
||||
argv = new_argv;
|
||||
}
|
||||
|
||||
return argv;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/***************************************************************************
|
||||
Process rename functions
|
||||
-----------------------------------------
|
||||
begin : 2013/06/21
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev 1 Team
|
||||
email : dev1@solbox.com
|
||||
version : 3.2.0
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __PROCESS_RENAME_H__
|
||||
#define __PROCESS_RENAME_H__
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief linux의 Process Title을 변경
|
||||
void set_ps_display(const char *activity, bool force);
|
||||
|
||||
/// @brief main에 argv의 실제 위치를 기억하고 새로은 메모리 활당하여 반환
|
||||
char ** save_ps_display_args(int argc, char **argv);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif // __PROCESS_RENAME_H__
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "ReportLog.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#define REPORT_LOG_NAME "report" // Report Log 폴더 및 파일 명칭 지정자..
|
||||
#define MAX_BUFFER_SIZE 2048 // 임시 버퍼 최대 크기
|
||||
|
||||
|
||||
// 생성자..
|
||||
CReportLog::CReportLog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// 소멸자..
|
||||
CReportLog::~CReportLog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CReportLog::Init( const char * szPath )
|
||||
{
|
||||
// 멤버 변수 초기화.
|
||||
m_strFullPath.clear();
|
||||
m_strLastError.clear();
|
||||
|
||||
// 변수 유효성 검사.
|
||||
if( szPath == NULL || strlen( szPath ) == 0 )
|
||||
{
|
||||
m_strLastError = "Log path empty.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log Path check...
|
||||
struct stat dirStat;
|
||||
if( lstat ( szPath, &dirStat ) != 0)
|
||||
{
|
||||
//m_strLastError = "Log path not valid. Check path [" + szPath + "][" + strerror(errno) + "]" ;
|
||||
m_strLastError = "Log path not valid. Check path [";
|
||||
m_strLastError.append( szPath );
|
||||
m_strLastError.append( "][" );
|
||||
m_strLastError.append( strerror(errno) );
|
||||
m_strLastError.append( "]" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 해당 정보가 Directory 가 아닌 경우
|
||||
if( S_ISDIR( dirStat.st_mode ) == 0 )
|
||||
{
|
||||
if( S_ISLNK( dirStat.st_mode ) == 0 )
|
||||
{
|
||||
//m_strLastError = "Log path not directory or link. Check path [" + szPath + "]";
|
||||
m_strLastError = "Log path not directory or link. Check path [" ;
|
||||
m_strLastError.append( szPath );
|
||||
m_strLastError.append ( "]" );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//m_strLastError = "Log path is symbolic link. path [" + szPath + "]";
|
||||
m_strLastError = "Log path is symbolic link. path [";
|
||||
m_strLastError.append( szPath );
|
||||
m_strLastError.append( "]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 멤버 변수 정보 설정.
|
||||
m_strFullPath = szPath;
|
||||
m_strFullPath.append( "/" );
|
||||
m_strFullPath.append( REPORT_LOG_NAME );
|
||||
|
||||
// 디렉토리 정보 검사
|
||||
if( lstat ( m_strFullPath.c_str(), &dirStat ) == 0)
|
||||
{
|
||||
// 해당 이름을 가진 파일 또는 디렉토리가 존재하고
|
||||
if( S_ISDIR( dirStat.st_mode ) != 0 )
|
||||
{
|
||||
// 해당 이름이 디렉토리인 경우
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 최종 경로에 대한 Directory 가 존재하지 않는 경우 Directory 생성 시도
|
||||
// 2013-11-27 report 로그는 fluentd 에 의해 감시, 분석되어야 하는데.. root 계정이 아닌 다른 td-agent 계정을 사용함.
|
||||
// 이로 인해 fluentd 에서 report 폴더에 접근할 수 있도록 폴더 권한 설정을 777 로 해야 함.
|
||||
if( mkdir( m_strFullPath.c_str(), 0777 ) != 0 )
|
||||
{
|
||||
m_strLastError = "Log directory create failed. Check path [" + m_strFullPath + "][" + strerror(errno) + "]";
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CReportLog::Write( const char * szLevel, const char * fmt, ...)
|
||||
{
|
||||
// Get Current Data & Time
|
||||
char szTimeString[256];
|
||||
time_t now = time( NULL );
|
||||
struct tm timeNow;
|
||||
localtime_r( &now, &timeNow );
|
||||
|
||||
// Make File Name
|
||||
std::string fileName = m_strFullPath + "/" + REPORT_LOG_NAME + "_";
|
||||
snprintf( szTimeString, (size_t)256, "%04d%02d%02d.log", timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday);
|
||||
fileName.append( szTimeString );
|
||||
|
||||
|
||||
// Log file open
|
||||
FILE * pFile = NULL;
|
||||
pFile = fopen( fileName.c_str(), "a+" );
|
||||
if( pFile == NULL )
|
||||
{
|
||||
m_strLastError = "Log file open fail.[" + fileName + "][" + strerror(errno) + "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 시간 정보처리
|
||||
snprintf( szTimeString, (size_t)256, "[%04d-%02d-%02d %02d:%02d:%02d]"
|
||||
, timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday
|
||||
, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec );
|
||||
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char szBuffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( szBuffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
fclose( pFile );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
|
||||
// Write to log file
|
||||
fprintf( pFile, "%s [%s] %s\n", szTimeString, szLevel, szBuffer );
|
||||
fflush( pFile );
|
||||
|
||||
// 종료 처리.
|
||||
fclose( pFile );
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
Report 로그 처리용 Class
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2013/11/25
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev 1 Team
|
||||
email : dev1@solbox.com
|
||||
version : 3.2.0
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __REPORT_LOG_H__
|
||||
#define __REPORT_LOG_H__
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
|
||||
|
||||
/// @brief CReportLog
|
||||
/// 본 클래스는 전달받은 경로 하위 report 폴더 상에...
|
||||
/// 운영자들에게 전달할 report 관련 log 를 기록하기 위한 클래스임.
|
||||
/// lib 에 존재하는 CLogger Class 가 프로세스에 대해 singleton 방식으로 동작하므로... 해당 Class 를 사용하기는 어려움.
|
||||
/// 따라서 report 관련 로그만 전담하여 처리하기 위한 로그 Class 를 추가함.
|
||||
class CReportLog
|
||||
{
|
||||
public:
|
||||
|
||||
// constructor
|
||||
CReportLog();
|
||||
|
||||
// destructor
|
||||
~CReportLog();
|
||||
|
||||
// 초기화..
|
||||
bool Init( const char * szPath );
|
||||
|
||||
// log wirte
|
||||
bool Write( const char * szLevel, const char * fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
// Get last error log
|
||||
void GetLastError( std::string & errorMessage ) { errorMessage = m_strLastError; return; }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Log Full Path
|
||||
std::string m_strFullPath;
|
||||
|
||||
// last Error String
|
||||
std::string m_strLastError;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __REPORT_LOG_H__ */
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
[COMMON]
|
||||
# Log Directory path
|
||||
DEFAULT_LOG_DIR = /user/service/logs
|
||||
|
||||
# Log write level: 7:debug 6:info(Default) 5:notice 4:warning 3:error 2:critical 1:alert 0:emergency
|
||||
LOG_LEVEL = 6
|
||||
|
||||
# RC ID
|
||||
RC_ID = nh61
|
||||
|
||||
# RCDB Access Information
|
||||
#RCDB_IP = 192.168.10.130
|
||||
RCDB_IP = 117.52.13.254
|
||||
RCDB_PORT = 6543
|
||||
RCDB_DB_NAME = localdb
|
||||
RCDB_ACCT = syshost
|
||||
RCDB_ACCT_PW = alfkzmf!?{}
|
||||
|
||||
# skip keyword : h|path (head) or m|path(middle) or t|path(tail)
|
||||
SKIP_KEYWORD = h|/INCINERATE,t|.ICS_TEMP,t|.ics_temp,t|.SB_TEMP,t|.sb_temp
|
||||
|
||||
#-----------------------------------------------#
|
||||
[rc_apid]
|
||||
|
||||
# Log write level: 7:debug 6:info(Default) 5:notice 4:warning 3:error 2:critical 1:alert 0:emergency
|
||||
LOG_LEVEL = 7
|
||||
|
||||
# FHS ftsd TCP service port (default: 14001)
|
||||
FHS_TRANSFER_DAEMON_PORT = 14001
|
||||
|
||||
# content size, MD5 hash info request Listen Port (default:14021)
|
||||
PORT_TCP_LISTEN_SIZE_HASH = 14021
|
||||
|
||||
#-----------------------------------------------#
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
#include "ContentSelect.h"
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
|
||||
CContentSelect::CContentSelect()
|
||||
{
|
||||
// thread id 정보 추출
|
||||
m_lThreadId = syscall( SYS_gettid );
|
||||
|
||||
}
|
||||
|
||||
CContentSelect::~CContentSelect()
|
||||
{
|
||||
// rcdb 와 의 연결을 명시적으로 해제
|
||||
m_rcdb.PgCloseDB();
|
||||
}
|
||||
|
||||
|
||||
bool CContentSelect::Start()
|
||||
{
|
||||
// RCDB 접속 정보 load
|
||||
m_rcdbInfo.Load();
|
||||
|
||||
// RCDB 와 연결을 시도한다.
|
||||
if( m_rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort, m_rcdbInfo.m_strRcdbName
|
||||
, m_rcdbInfo.m_strRcdbAcct, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
|
||||
{
|
||||
// 연결 실패시...
|
||||
_LOG( LERR, "[ContentSelect %d %ld] RCDB connection failed. [%s:%u]"
|
||||
, getpid(), m_lThreadId, m_rcdbInfo.m_strRcdbIp.c_str(), m_rcdbInfo.m_nRcdbPort );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RCDB 연결 정상 처리된 경우...
|
||||
_LOG( LDBG, "[ContentSelect %d %ld] RCDB connection ok. [%s:%u]"
|
||||
, getpid(), m_lThreadId
|
||||
, m_rcdbInfo.m_strRcdbIp.c_str(), m_rcdbInfo.m_nRcdbPort );
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CContentSelect::Exit()
|
||||
{
|
||||
// rcdb 와 의 연결을 명시적으로 해제
|
||||
m_rcdb.PgCloseDB();
|
||||
}
|
||||
|
||||
bool CContentSelect::GetHostSizeFilename( const std::string & szServiceSeq, const bool & bUriIgnoreCase
|
||||
, const std::string & szUri
|
||||
, std::string & szErrorMessage, std::vector<struct HostSizeFilenameInfo> & vecResult )
|
||||
{
|
||||
// RCDB 는 이미 연결된 상태
|
||||
char szQuery[8192];
|
||||
int nResult = 0;
|
||||
|
||||
if( bUriIgnoreCase == true )
|
||||
{
|
||||
// 대소문자 무시 서비스인 경우
|
||||
snprintf( szQuery, 8192 - 1
|
||||
, "SELECT host_name, filename_hash, get_content_length "
|
||||
"FROM t_meta_%s "
|
||||
"WHERE resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' "
|
||||
" AND uri = lower('%s') "
|
||||
"ORDER BY is_cache ASC ;" // 원본부터 우선 조회되도록 sort.
|
||||
, szServiceSeq.c_str(), szUri.c_str() );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// 대소문자 구분 서비스인 경우
|
||||
snprintf( szQuery, 8192 - 1
|
||||
, "SELECT host_name, filename_hash, get_content_length "
|
||||
"FROM t_meta_%s "
|
||||
"WHERE resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' "
|
||||
" AND uri = '%s' "
|
||||
"ORDER BY is_cache ASC ;" // 원본부터 우선 조회되도록 sort.
|
||||
, szServiceSeq.c_str(), szUri.c_str() );
|
||||
}
|
||||
|
||||
// Query 실행
|
||||
m_rcdb.PgDoExec( szQuery );
|
||||
|
||||
nResult = m_rcdb.PgResult( DataBase::NOT_CLEAR );
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
szErrorMessage = "db error.";
|
||||
|
||||
_LOG( LERR, "[ContentSelect %d %ld] content select exec error. [%d][%s][%s]"
|
||||
, getpid(), m_lThreadId
|
||||
, nResult, m_rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
m_rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 결과 Row 수 확인
|
||||
nResult = m_rcdb.GetNoTuples();
|
||||
_LOG( LDBG, "[ContentSelect %d %ld] rcdb content select result[%d %s]"
|
||||
, getpid(), m_lThreadId
|
||||
, nResult, szQuery );
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
szErrorMessage = "db select error.";
|
||||
|
||||
_LOG( LERR, "[ContentSelect %d %ld] content select tuple error. [%d %s %s]"
|
||||
, getpid(), m_lThreadId
|
||||
, nResult, szUri.c_str(), m_rcdb.GetErrorMessage().c_str() );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
m_rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
szErrorMessage = "content not found from db.";
|
||||
|
||||
// 해당 Content 가 없는 경우..
|
||||
_LOG( LWAR, "[ContentSelect %d %ld] content not found from db. [%s]"
|
||||
, getpid(), m_lThreadId
|
||||
, szUri.c_str() );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
m_rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct HostSizeFilenameInfo data;
|
||||
vecResult.clear();
|
||||
|
||||
// 조회 결과를 output 변수에 저장 처리
|
||||
for( int i = 0; i < nResult; i++ )
|
||||
{
|
||||
// 0: host_name
|
||||
// 1: filename_hash
|
||||
// 2: get_content_length
|
||||
|
||||
data.uContentLength = atoll( m_rcdb.GetValue( i, 2 ) );
|
||||
data.szHost = m_rcdb.GetValue( i, 0 );
|
||||
data.szFilename = m_rcdb.GetValue( i, 1 );
|
||||
|
||||
_LOG( LINF, "[ContentSelect %d %ld] content select [%s]: [%s %llu %s]"
|
||||
, getpid(), m_lThreadId
|
||||
, szUri.c_str(), data.szHost.c_str(), data.uContentLength, data.szFilename.c_str() );
|
||||
|
||||
vecResult.push_back( data );
|
||||
}
|
||||
}
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
m_rcdb.PgClear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/****************************************************************************
|
||||
RCDB 로 부터 전달받은 content 정보 조회를 담당하는 class
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2021/03/15
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __CONTENT_SELECT_H__
|
||||
#define __CONTENT_SELECT_H__
|
||||
|
||||
|
||||
#include "Database.h"
|
||||
#include "RcdbInfo.h"
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
// host, size, filename_hash 정보 저장을 위한 구조체
|
||||
struct HostSizeFilenameInfo {
|
||||
|
||||
std::string szHost;
|
||||
unsigned long long uContentLength;
|
||||
std::string szFilename;
|
||||
|
||||
};
|
||||
|
||||
// RCDB 에서 Content 정보 조회 처리를 담당하는 class.
|
||||
class CContentSelect
|
||||
{
|
||||
public:
|
||||
|
||||
CContentSelect();
|
||||
|
||||
~CContentSelect();
|
||||
|
||||
// 초기화 처리
|
||||
bool Start();
|
||||
|
||||
// 최종 정리시 호출
|
||||
void Exit();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// RCDB 연결 정보 관리 객체
|
||||
CRcdbInfo m_rcdbInfo;
|
||||
|
||||
// RCDB 연결 객체
|
||||
DataBase m_rcdb;
|
||||
|
||||
// log 기록시 사용할 thread id 정보
|
||||
long m_lThreadId;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// RCDB 로 부터 uri 에 해당하는 hostname, content_length, filename_hash 정보를 반환.
|
||||
bool GetHostSizeFilename( const std::string & szServiceSeq, const bool & bUriIgnoreCase, const std::string & szUri
|
||||
, std::string& szErrorMessage, std::vector<struct HostSizeFilenameInfo> & vecResult );
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __CONTENT_SELECT_H__ */
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/***************************************************************************
|
||||
Database Class
|
||||
-----------------------------------------
|
||||
begin : 2010/03/09
|
||||
copyright : (C) 2010 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of SolutionBox Inc.
|
||||
***************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sstream>
|
||||
#include "Database.h"
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
DataBase::~DataBase()
|
||||
{
|
||||
if(m_PGconn != NULL) {
|
||||
PgCloseDB();
|
||||
}
|
||||
}
|
||||
|
||||
PGconn *DataBase::PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout)
|
||||
{
|
||||
ostringstream conninfo;
|
||||
|
||||
// CHG 2014-10-29 huibong
|
||||
// application_name add to logging RCDB
|
||||
|
||||
conninfo << "host=" << strHost << " port=" << Port << " dbname=" << strDBName <<
|
||||
" user=" << strAcct << " password=" << strPasswd << " connect_timeout=" << timeout <<
|
||||
" application_name=" << PROG_NAME;
|
||||
|
||||
#ifdef _USE_LIBPQ_KEEPALIVE
|
||||
// This option is supported by at least 9.1.2. and Currently supports Linux systems.
|
||||
conninfo <<" keepalives=1";
|
||||
#endif // _USE_LIBPQ_KEEPALIVE
|
||||
//m_PGconn = PQsetdbLogin(strHost.c_str(), szPort, NULL, NULL, strDBName.c_str(), strAcct.c_str(), strPasswd.c_str());
|
||||
m_PGconn = PQconnectdb(conninfo.str().c_str());
|
||||
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_PGconn;
|
||||
}
|
||||
|
||||
PGconn *DataBase::PgOpenDB(const char *pszDBName)
|
||||
{
|
||||
m_PGconn = PQsetdb(NULL, NULL, NULL, NULL, pszDBName);
|
||||
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
|
||||
return NULL;
|
||||
}
|
||||
return m_PGconn;
|
||||
}
|
||||
|
||||
void DataBase::PgCloseDB()
|
||||
{
|
||||
if(m_PGconn != NULL)
|
||||
{
|
||||
PQfinish(m_PGconn);
|
||||
m_PGconn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int DataBase::PgResult(CFLAG flag)
|
||||
{
|
||||
int fRet = 0;
|
||||
int Result;
|
||||
|
||||
Result = PQresultStatus(m_pRes);
|
||||
switch(Result) {
|
||||
case PGRES_EMPTY_QUERY :
|
||||
fRet = -1;
|
||||
break;
|
||||
case PGRES_BAD_RESPONSE :
|
||||
fRet = -2;
|
||||
break;
|
||||
case PGRES_NONFATAL_ERROR :
|
||||
fRet = -3;
|
||||
break;
|
||||
case PGRES_FATAL_ERROR :
|
||||
fRet = -4;
|
||||
break;
|
||||
case PGRES_TUPLES_OK :
|
||||
fRet = 1;
|
||||
break;
|
||||
case PGRES_COMMAND_OK :
|
||||
fRet = 2;
|
||||
break;
|
||||
}
|
||||
if(fRet < 0) {
|
||||
m_ErrorMessage = PQresultErrorMessage(m_pRes);
|
||||
}
|
||||
m_ResultCode = Result;
|
||||
if(flag == CLEAR && m_pRes) {
|
||||
PQclear(m_pRes);
|
||||
m_pRes = NULL;
|
||||
}
|
||||
return fRet;
|
||||
}
|
||||
|
||||
string &DataBase::GetErrorMessage()
|
||||
{
|
||||
return m_ErrorMessage;
|
||||
}
|
||||
|
||||
int DataBase::GetCmdTuples()
|
||||
{
|
||||
//fprintf(stderr, "DataBase::GetCmdTuples PQcmdTuples %s\n", PQcmdTuples(m_pRes));
|
||||
return (int)atoi(PQcmdTuples(m_pRes));
|
||||
}
|
||||
|
||||
int DataBase::GetNoTuples()
|
||||
{
|
||||
return PQntuples(m_pRes);
|
||||
}
|
||||
|
||||
int DataBase::GetNoFields()
|
||||
{
|
||||
return PQnfields(m_pRes);
|
||||
}
|
||||
|
||||
PGresult *DataBase::GetRes()
|
||||
{
|
||||
return m_pRes;
|
||||
}
|
||||
|
||||
void DataBase::PgClear()
|
||||
{
|
||||
if(m_pRes)
|
||||
{
|
||||
PQclear(m_pRes);
|
||||
m_pRes = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
char *DataBase::GetValue(int tuple, int field)
|
||||
{
|
||||
return PQgetvalue(m_pRes, tuple, field);
|
||||
}
|
||||
|
||||
int DataBase::PgDoExec(char *pszQuery)
|
||||
{
|
||||
return this->PgDoExec(pszQuery, NOT_CLEAR);
|
||||
}
|
||||
|
||||
int DataBase::PgDoExec(string &strQuery)
|
||||
{
|
||||
return this->PgDoExec((char *)strQuery.c_str(), NOT_CLEAR);
|
||||
}
|
||||
|
||||
int DataBase::PgDoExec(char *pszQuery, CFLAG flag)
|
||||
{
|
||||
if(m_PGconn == NULL) return -1;
|
||||
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
|
||||
m_pRes = PQexec(m_PGconn, pszQuery);
|
||||
int r = PgResult(flag);
|
||||
return r;
|
||||
}
|
||||
|
||||
int DataBase::PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag)
|
||||
{
|
||||
if(m_PGconn == NULL) return -1;
|
||||
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
|
||||
m_pRes = PQexecParams(m_PGconn, pszQuery, nParamCnt, NULL, paramValues, NULL, NULL, 0);
|
||||
int r = PgResult(flag);
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
int DataBase::PgEscapeString(char *to, const char *from, size_t length)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
//PQescapeStringConn(m_PGconn, to, from, length, &retval);
|
||||
PQescapeString(to, from, length);
|
||||
return retval;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/***************************************************************************
|
||||
Database Class
|
||||
-----------------------------------------
|
||||
begin : 2010/03/11
|
||||
copyright : (C) 2010 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of SolutionBox Inc.
|
||||
***************************************************************************/
|
||||
#ifndef __POSTGRESQL_DATABASE_H__
|
||||
#define __POSTGRESQL_DATABASE_H__
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "libpq-fe.h"
|
||||
#define MaxSizeOfDBQuery 1024*9
|
||||
|
||||
using namespace std;
|
||||
class DataBase
|
||||
{
|
||||
public:
|
||||
enum CFLAG { CLEAR = 1, NOT_CLEAR };
|
||||
DataBase() { m_PGconn = NULL; m_pRes = NULL;}
|
||||
~DataBase();
|
||||
PGconn *PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout = 10);
|
||||
PGconn *PgOpenDB(const char *pszDBName);
|
||||
void PgCloseDB();
|
||||
int PgResult(CFLAG flag);
|
||||
PGconn *GetPgConn(){ return m_PGconn;}
|
||||
PGresult *GetRes();
|
||||
void SetRes(PGresult * v) {m_pRes = v;}
|
||||
int GetCmdTuples();
|
||||
int GetNoTuples();
|
||||
int GetNoFields();
|
||||
int GetResultCode() { return m_ResultCode; }
|
||||
char *GetValue(int tuple, int field);
|
||||
void PgClear();
|
||||
int PgDoExec(string &strQuery);
|
||||
int PgDoExec(char *pszQuery);
|
||||
int PgDoExec(char *pszQuery, CFLAG flag);
|
||||
int PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag = NOT_CLEAR);
|
||||
int PgEscapeString(char *to, const char *from, size_t length);
|
||||
string &GetErrorMessage();
|
||||
private:
|
||||
int m_ResultCode;
|
||||
PGconn *m_PGconn;
|
||||
PGresult *m_pRes;
|
||||
string m_ErrorMessage;
|
||||
};
|
||||
|
||||
#endif // ~__POSTGRESQL_DATABASE_H__
|
||||
@@ -0,0 +1,579 @@
|
||||
#include "Main.h"
|
||||
|
||||
|
||||
#include "ProcessRename.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include "WorkerSizeHash.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
// TCP listen socket 생성 후 listen 처리시 accept 대기 Count 수
|
||||
#define DEFAULT_ACCEPT_WAIT_COUNT 50
|
||||
|
||||
|
||||
// 고객사에서 요청하는 Content size, md5 hash 정보 처리를 위한 TCP listen socket
|
||||
// 시그널 처리 및 Worker 에서 처리를 단순화 하기 위해 전역 변수로 처리.
|
||||
int g_listenSocketSizeHash = -1;
|
||||
|
||||
// Worker 프로세스 중 고객사에서 요청하는 Content size, md5 hash 정보 처리를 담당할 Worker PID 정보.
|
||||
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리
|
||||
pid_t g_pidWorkerSizeHash = -1;
|
||||
|
||||
|
||||
|
||||
int main( int argc, char * argv[] )
|
||||
{
|
||||
// 설정 파일을 저장할 변수.
|
||||
std::string strConfigFileName = DEFAULT_CONFIG_FILE;
|
||||
|
||||
// linux 에서 프로세스를 Titile 변경을 지원을 위해서 argv 메모리에 저장
|
||||
argv = save_ps_display_args( argc, argv );
|
||||
|
||||
// 전달 받은 옵션 여부 확인 및 처리
|
||||
if( argc >= 2 )
|
||||
{
|
||||
int opt;
|
||||
while( ( opt = getopt( argc, argv, "hvc:" ) ) != -1 ) // 옵션 끝까지 파싱처리
|
||||
{
|
||||
switch( opt )
|
||||
{
|
||||
case 'h':
|
||||
case '?': // 정의되지 않은 문자가 나타날 경우 getopt 에 자동반환. 도움말 표시 처리.
|
||||
PrintUsage();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
case 'v':
|
||||
PrintVersion();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
case 'c':
|
||||
strConfigFileName = optarg;
|
||||
fprintf( stderr, PROG_NAME " start with user define conf[%s]\n", strConfigFileName.c_str() );
|
||||
break;
|
||||
|
||||
default: // 본 조건절은 getopt 특성상 동작하지 않지만...업무 Flow 이해(?)를 위해 유지한다.
|
||||
fprintf( stderr, "Unkonwn options[%c] used. program terminated.\n", opt );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 프로세스 중복 실행 여부 검사
|
||||
if( IsCurrentProcessRun() == true )
|
||||
{
|
||||
fprintf( stderr, "[warning] Process [" PROG_NAME "] is already running....\n" );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::string szErrorMessage;
|
||||
|
||||
// conf 로딩 및 검사
|
||||
if( CProcessConfig::Init( PROG_NAME, strConfigFileName, szErrorMessage ) == false )
|
||||
{
|
||||
fprintf( stderr, "[ERR] %s\n\n", szErrorMessage.c_str() );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Log 객체 생성 및 초기화
|
||||
if( CLogger::Init( PROG_NAME, CProcessConfig::GetInstance()->GetLogPath(), LINF ) == false )
|
||||
{
|
||||
fprintf( stderr, "[ERR] Log module initialize failed.[%s]\n\n", CProcessConfig::GetInstance()->GetLogPath() );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Daemonize...
|
||||
if( daemon( 1, 0 ) == -1 ) // nochdir: true(작업 디렉토리 변경 안함), noclose: false (표준 입출력, 에러를 /dev/null 로 리디렉트 처리)
|
||||
{
|
||||
fprintf( stderr, "[ERR] Process daemonize failed.[%s]\n\n", strerror( errno ) );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// signal 처리 설정
|
||||
SetSignalMain();
|
||||
|
||||
// Process 기동 관련 정보 기록 -> Log
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
_LOG( LINF, " %s Start. Version: %s", PROG_NAME, PROG_VERSION );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
_LOG( LINF, "Config : %s", strConfigFileName.c_str() );
|
||||
_LOG( LINF, "Log : %s/%s", CProcessConfig::GetInstance()->GetLogPath(), PROG_NAME );
|
||||
_LOG( LINF, "Log level : %d", CProcessConfig::GetInstance()->GetLogLevel() );
|
||||
_LOG( LINF, "RC ID : %s", CProcessConfig::GetInstance()->GetRcId() );
|
||||
_LOG( LINF, "RCDB : %s %u %s %s", CProcessConfig::GetInstance()->GetRcdbIp()
|
||||
, CProcessConfig::GetInstance()->GetRcdbPort()
|
||||
, CProcessConfig::GetInstance()->GetRcdbName()
|
||||
, CProcessConfig::GetInstance()->GetRcdbAcct() );
|
||||
|
||||
_LOG( LINF, "FHS ftsd tcp port : %u", CProcessConfig::GetInstance()->GetFhsTransferDaemonPort() );
|
||||
_LOG( LINF, "content size, md5 API port : %u", CProcessConfig::GetInstance()->GetSizeHashListenPort() );
|
||||
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
|
||||
// Log Level 재설정. -> conf 설정대로 변경 처리.
|
||||
#ifdef _DEBUG_
|
||||
CLogger::GetInstance()->SetLogLevel( LDBG );
|
||||
#else
|
||||
CLogger::GetInstance()->SetLogLevel( CProcessConfig::GetInstance()->GetLogLevel() );
|
||||
#endif
|
||||
|
||||
|
||||
// 통신 관련 Listen 등의 처리가 필요한 Socket 생성 처리.
|
||||
// worker 모듈에서 처리할 경우... 재기동 발생시.. 해당 port 점유로 인한 재사용 등의 오류가 발생하므로....
|
||||
|
||||
|
||||
// 고객사에서 요청하는 Content size, md5 hash 정보 처리를 위한 TCP listen socket 생성
|
||||
if( MakeSizeHashListenSocket() == false )
|
||||
{
|
||||
LOG( LERR, "content size, hash API listen socket create failed. => Process exit." );
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Worker Process 생성
|
||||
|
||||
// content size, hash 정보 제공 API worker 프로세스 생성
|
||||
if( MakeWorkerSizeHash() == false )
|
||||
{
|
||||
LOG( LERR, "[Main %d] content size, hash API worker process create failed. => Process exit.", getpid() );
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Worker 생성되면.. worker 초기화 작업을 위해 약 2 sec 정도 대기
|
||||
sleep( 2 );
|
||||
|
||||
// Process Rename..
|
||||
set_ps_display( PROG_NAME": Main", false );
|
||||
|
||||
|
||||
// Main Process : 그냥 대기
|
||||
while( 1 )
|
||||
{
|
||||
// Main Process 는 할 일이 없다.
|
||||
// => Worker 프로세스 종료시 SIGCHLD 신호로 인해 신호처리기에서 Worker Process 재성성 수행함.
|
||||
// => 따라서 그냥 시그널 대기
|
||||
pause();
|
||||
}
|
||||
|
||||
// 다음의 코드는 Daemon 으로 동작하기 때문에 수행되지 않는다.
|
||||
ReadyToExit();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 사용방법 표시
|
||||
void PrintUsage()
|
||||
{
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, "Usage: " PROG_NAME " [-h] [-v] [-c {file}] \n" );
|
||||
fprintf( stderr, "Options: \n" );
|
||||
fprintf( stderr, " -h Display help information \n" );
|
||||
fprintf( stderr, " -v Display version \n" );
|
||||
fprintf( stderr, " -c {file} Use {file} as config file \n" );
|
||||
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, PROG_NAME " is Solbox Cloud Storage module.\n" );
|
||||
fprintf( stderr, " - customer API support module.\n" );
|
||||
fprintf( stderr, " -- content size, MD5 hash value extraction support.\n\n" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 버전 정보 표시
|
||||
void PrintVersion()
|
||||
{
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, PROG_NAME " version: " PROG_VERSION "\n\n" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
|
||||
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
|
||||
bool IsCurrentProcessRun()
|
||||
{
|
||||
char tempBuffer[512];
|
||||
FILE * fd = NULL;
|
||||
bool bRun = false;
|
||||
|
||||
snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -x %s | sort", PROG_NAME );
|
||||
|
||||
fd = popen( tempBuffer, "r" );
|
||||
if( fd == NULL )
|
||||
{
|
||||
std::cerr << "[error] Process duplication check failed.[popen error][" << strerror( errno ) << "]" << std::endl;
|
||||
|
||||
// 오류 발생시 true 반환하여 프로세스 실행 방지처리
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
while( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
|
||||
{
|
||||
std::string tempPid( tempBuffer );
|
||||
Trim( tempPid );
|
||||
|
||||
if( atoi( tempPid.c_str() ) != getpid() )
|
||||
{
|
||||
std::cout << "[info] Process duplication found. pid[" << atoi( tempPid.c_str() ) << "]" << std::endl;
|
||||
|
||||
bRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pclose( fd );
|
||||
return bRun;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief std 상에 trim 함수가 없어서 직접 구현. 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
|
||||
/// @return void
|
||||
void Trim( std::string & str )
|
||||
{
|
||||
if( str.length() == 0 )
|
||||
return;
|
||||
|
||||
// 문자열 뒤의 공백, TAB, CR 등의 문자 제거처리.
|
||||
std::string::size_type pos = str.find_last_not_of( " \a\b\f\n\r\t\v" );
|
||||
if( pos != std::string::npos )
|
||||
str.erase( pos + 1 );
|
||||
|
||||
// 문자열 앞의 공백, TAB, CR 등의 문자 제거처리.
|
||||
pos = str.find_first_not_of( " \a\b\f\n\r\t\v" );
|
||||
if( pos != std::string::npos )
|
||||
str.erase( 0, pos );
|
||||
}
|
||||
|
||||
|
||||
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
|
||||
void ReadyToExit()
|
||||
{
|
||||
// open 된 socket 관련 Close 처리
|
||||
if( g_listenSocketSizeHash != -1 )
|
||||
close( g_listenSocketSizeHash );
|
||||
|
||||
// Logger 객체 종료 처리.
|
||||
CLogger::Exit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Worker 프로세스 종료 신호 수신시
|
||||
static void SignalWorkerDead( int nSignalNumber )
|
||||
{
|
||||
pid_t deadPid;
|
||||
int nDeadStatus;
|
||||
|
||||
while( ( deadPid = waitpid( -1, &nDeadStatus, WNOHANG ) ) > 0 )
|
||||
{
|
||||
// Worker 프로세스가 signal 에 의해 종료되었는지 검사.
|
||||
// - 종료 사유 로깅 및
|
||||
// - Worker 의 초기화 실패로 인한 종료시... Main 프로세스가 종료되도록 처리.
|
||||
if( WIFSIGNALED( nDeadStatus ) )
|
||||
{
|
||||
// signal 에 의해 종료된 경우...
|
||||
// - 관련 내역 로깅
|
||||
if( deadPid == g_pidWorkerSizeHash )
|
||||
{
|
||||
_LOG( LWAR, "[Main %d] content size, hash API worker process[%d] killed by signal[%d]"
|
||||
, getpid(), deadPid, WTERMSIG( nDeadStatus ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main %d] Unknown child process[%d] killed by signal[%d]"
|
||||
, getpid(), deadPid, WTERMSIG( nDeadStatus ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// signal 에 종료된 상황이 아닌 경우...
|
||||
// - 각 Worker 의 초기화 실패 발생시... return 구문에 의해 종료됨.
|
||||
// - 각 Worker 가 signal 을 받아서.. 정상적으로 return 처리하여 종료된 경우.
|
||||
// - 기타 등등
|
||||
|
||||
// 관련 내역 로깅.
|
||||
if( deadPid == g_pidWorkerSizeHash )
|
||||
{
|
||||
_LOG( LWAR, "[Main %d] content size, hash API worker process[%d] exited.", getpid(), deadPid );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main %d] Unknown child process[%d] exited.", getpid(), deadPid );
|
||||
}
|
||||
|
||||
// 만약 Worker 프로세스들이 초기화 실패로 인해 EXIT_FAILURE 반환되는 상황이라면...
|
||||
// 자식 프로세스를 재생성하지 않고.. 프로세스를 종료 처리한다.
|
||||
if( WIFEXITED( nDeadStatus ) )
|
||||
{
|
||||
if( WEXITSTATUS( nDeadStatus ) == EXIT_FAILURE )
|
||||
{
|
||||
if( deadPid == g_pidWorkerSizeHash )
|
||||
{
|
||||
_LOG( LWAR, "[Main %d] content size, hash API worker process[%d] initialize failed.", getpid(), deadPid );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main %d] Unknown child process[%d] exit with fail.", getpid(), deadPid );
|
||||
}
|
||||
|
||||
_LOG( LERR, "[Main %d] Program abnormally exited because Worker initialize faild.", getpid() );
|
||||
|
||||
// Main 종료 시그널 발생
|
||||
raise( SIGTERM );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 위에서 로깅 및 초기화 실패로 인한 Main 종료 처리 작업을 수행
|
||||
// 그 외의 경우에는 다음과 같이 한다.
|
||||
// - 심각한 문제가 발생한 경우 일 경우에는... 전체 프로세스를 종료 처리한다.
|
||||
// - 그 외에는 Worker 를 재생성 처리한다.
|
||||
if( deadPid == g_pidWorkerSizeHash )
|
||||
{
|
||||
if( MakeWorkerSizeHash() == false )
|
||||
{
|
||||
_LOG( LERR, "[Main %d] content size, hash API worker process recreate failed.=> Main process exit.", getpid() );
|
||||
raise( SIGTERM );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main %d] content size, hash API worker process recreate success.", getpid() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 오류 발생시 해당 내역 로깅
|
||||
if( deadPid < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "[Main %d] SIG_CHLD receive but waitpid return error[%d][%s]"
|
||||
, getpid(), errorNum, strerror( errorNum ) );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Main 프로세스에 대한 종료 처리 수신시
|
||||
static void SignalMainTerminate( int nSignalNumber )
|
||||
{
|
||||
// 자식 프로세스를 종료 처리한다.
|
||||
if( g_pidWorkerSizeHash != -1 )
|
||||
{
|
||||
kill( g_pidWorkerSizeHash, SIGTERM );
|
||||
}
|
||||
|
||||
// Signal Number 에 따른 로깅처리.
|
||||
if( nSignalNumber == SIGTERM )
|
||||
{
|
||||
_LOG( LINF, "[Main %d] Process exit by SIGTERM signal. Good Bye..", getpid() );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main %d] Process exit by abnormal signal[%d]. Good Bye..", getpid(), nSignalNumber );
|
||||
}
|
||||
|
||||
// 잠시 대기
|
||||
sleep( 1 );
|
||||
|
||||
// 기타 모듈들의 종료 처리 수행
|
||||
ReadyToExit();
|
||||
|
||||
// 종료전 잠시 대기
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
|
||||
// Main Process 의 signal 처리기...
|
||||
// fork 된 worker 프로세스 역시 기본적으로 본 signal 처리 action 을 상속받는다.
|
||||
void SetSignalMain()
|
||||
{
|
||||
sigset_t set;
|
||||
struct sigaction act;
|
||||
|
||||
memset( &act, 0x00, sizeof( act ) );
|
||||
sigfillset( &set );
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
sigfillset( &act.sa_mask );
|
||||
|
||||
// 무시 처리 signal
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction( SIGPIPE, &act, NULL ); /* desciptor 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
|
||||
sigaction( SIGHUP, &act, NULL ); /* process를 기동시킨 관리자의 로그아웃시, 또는 config reload 등의 처리 signal*/
|
||||
sigaction( SIGINT, &act, NULL ); /* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
|
||||
sigaction( SIGQUIT, &act, NULL ); /* 키보드에 의한 Abort 신호 처리 => ? */
|
||||
|
||||
// Worker child process 종료에 대한 처리기 설정.
|
||||
act.sa_handler = SignalWorkerDead;
|
||||
sigaction( SIGCHLD, &act, NULL );
|
||||
|
||||
// 사용자의 종료 또는 에러 관련 신호 처리.
|
||||
act.sa_handler = SignalMainTerminate;
|
||||
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
|
||||
|
||||
// 나머지 신호는 default 처리.
|
||||
|
||||
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
}
|
||||
|
||||
// content size, hash 정보 제공 API worker 프로세스 생성.
|
||||
bool MakeWorkerSizeHash()
|
||||
{
|
||||
// fork() 결과를 RecvSystem Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장.
|
||||
g_pidWorkerSizeHash = fork();
|
||||
|
||||
if( g_pidWorkerSizeHash == 0 )
|
||||
{
|
||||
// 생성된 Worker 프로세스인 경우...
|
||||
int iResult;
|
||||
|
||||
// content size, hash 정보 제공 API worker 프로세스의 Main 함수 호출
|
||||
iResult = WorkerSizeHashMain();
|
||||
|
||||
// content size, hash 정보 제공 API worker 프로세스의 Main 함수 종료시 종료 처리
|
||||
_LOG( LINF, "[Worker %d] content size, hash API worker process exit. return[%d]", getpid(), iResult );
|
||||
|
||||
ReadyToExit();
|
||||
exit( iResult );
|
||||
}
|
||||
else if( g_pidWorkerSizeHash < 0 )
|
||||
{
|
||||
// fork 실패시
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "[Main %d] content size, hash API worker process create failed. [%d][%s]"
|
||||
, getpid(), errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 현재의 Main 프로세스
|
||||
_LOG( LINF, "[Main %d] content size, hash API worker process created.. PID[%d]", getpid(), g_pidWorkerSizeHash );
|
||||
|
||||
// 잠시 대기
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 200000000; // 0.2 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// content size, hash 정보 제공 API 관련 request 수신을 위한 TCP listen socket 생성
|
||||
bool MakeSizeHashListenSocket()
|
||||
{
|
||||
// 기존 Socket close 처리.
|
||||
if( g_listenSocketSizeHash != -1 )
|
||||
{
|
||||
close( g_listenSocketSizeHash );
|
||||
g_listenSocketSizeHash = -1;
|
||||
}
|
||||
|
||||
// TCP Socket 생성 처리.
|
||||
g_listenSocketSizeHash = socket( AF_INET, SOCK_STREAM, 0 );
|
||||
if( g_listenSocketSizeHash < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "content size, hash API listen socket create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Socket option 설정 처리.
|
||||
int result = 0;
|
||||
int opt = 1;
|
||||
|
||||
// socket port reuse option set
|
||||
opt = 1;
|
||||
result = setsockopt( g_listenSocketSizeHash, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof( opt ) );
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "content size, hash API listen socket option[SO_REUSEADDR] set failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
close( g_listenSocketSizeHash );
|
||||
g_listenSocketSizeHash = -1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep Alive Option set
|
||||
opt = 1;
|
||||
result = setsockopt( g_listenSocketSizeHash, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) );
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "content size, hash API listen socket option[SO_KEEPALIVE] set failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
// bind 처리를 위한 설정
|
||||
struct sockaddr_in listenSocketAddr;
|
||||
socklen_t socketAddrLen = 0;
|
||||
|
||||
memset( &listenSocketAddr, 0x00, sizeof( listenSocketAddr ) );
|
||||
socketAddrLen = sizeof( listenSocketAddr );
|
||||
|
||||
listenSocketAddr.sin_family = AF_INET;
|
||||
listenSocketAddr.sin_port = htons( CProcessConfig::GetInstance()->GetSizeHashListenPort() );
|
||||
listenSocketAddr.sin_addr.s_addr = htonl( INADDR_ANY );
|
||||
|
||||
// bind 처리.
|
||||
result = bind( g_listenSocketSizeHash, (struct sockaddr *)&listenSocketAddr, socketAddrLen );
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "content size, hash API listen socket bind[%u] failed.[%d][%s]"
|
||||
, CProcessConfig::GetInstance()->GetSizeHashListenPort(), errorNum, strerror( errorNum ) );
|
||||
|
||||
close( g_listenSocketSizeHash );
|
||||
g_listenSocketSizeHash = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Socket Listen
|
||||
result = listen( g_listenSocketSizeHash, DEFAULT_ACCEPT_WAIT_COUNT );
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "content size, hash API listen socket listen failed.[%d][%s]"
|
||||
, errorNum, strerror( errorNum ) );
|
||||
|
||||
close( g_listenSocketSizeHash );
|
||||
g_listenSocketSizeHash = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// logging 처리
|
||||
_LOG( LINF, "content size, hash API listen socket create success. tcp port[%u]", CProcessConfig::GetInstance()->GetSizeHashListenPort() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/****************************************************************************
|
||||
rc_apid header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2021/03/09
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __RC_APID_MAIN_H__
|
||||
#define __RC_APID_MAIN_H__
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/// @brief 사용 방법을 화면에 표시하기 위한 함수.
|
||||
void PrintUsage( void );
|
||||
|
||||
/// @brief Version 정보를 화면에 표시하기 위한 함수
|
||||
void PrintVersion( void );
|
||||
|
||||
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
|
||||
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
|
||||
bool IsCurrentProcessRun( void );
|
||||
|
||||
/// @brief std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
|
||||
/// @param str [in/out] Trim 할 문자열을 저장한 String 참조변수.
|
||||
/// @return void
|
||||
void Trim( std::string & str );
|
||||
|
||||
/// @brief 프로세스 종료에 따른 반복적인 종료 관련 작업을 수행하기 위한 함수.
|
||||
void ReadyToExit( void );
|
||||
|
||||
/// @brief Main 프로세스 signal 처리 설정을 위한 함수
|
||||
/// @return void
|
||||
void SetSignalMain( void );
|
||||
|
||||
|
||||
// content size, hash 정보 제공 API 관련 request 수신을 위한 TCP listen socket 생성
|
||||
bool MakeSizeHashListenSocket( void );
|
||||
|
||||
/// @brief content size, hash 정보 제공 API worker 프로세스 생성.
|
||||
/// @return 생성 성공시 true, 실패시 false
|
||||
bool MakeWorkerSizeHash();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* __RC_APID_MAIN_H__ */
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#****************************************************************************
|
||||
# Makefile for rc_apid
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2021/03/09
|
||||
# copyright : (C) 2005 Solbox Inc.
|
||||
# author : Dev Storage Team
|
||||
# email : huibong@solbox.com
|
||||
# version : 3.5
|
||||
#
|
||||
# CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
# Redistribution and use in source and binary forms, with or with out
|
||||
# modification, are not permitted in outside of Solbox Inc.
|
||||
#****************************************************************************
|
||||
|
||||
|
||||
# Program info
|
||||
PROG_NAME = rc_apid
|
||||
REVISION = 1533
|
||||
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
|
||||
|
||||
DEFAULT_CONFIG_FILE = /user/service/etc/rcts.conf
|
||||
|
||||
INSTALL_BIN = /user/service/bin
|
||||
INSTALL_CONF = /user/service/etc
|
||||
|
||||
# Compiler info
|
||||
CC = /usr/bin/g++
|
||||
|
||||
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized \
|
||||
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual \
|
||||
-fno-rtti -D_REENTRANT -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
|
||||
LFLAGS =
|
||||
|
||||
DFLAGS = -DPROG_NAME=\"$(PROG_NAME)\" -DPROG_VERSION=\"$(PROG_VERSION)\" -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\"
|
||||
|
||||
# Application Enviroment
|
||||
APP = $(PROG_NAME)
|
||||
|
||||
DIR_INCLUDE = -I../lib -I../ftsd -I/user/db/pgsql/include
|
||||
DIR_LIB = -L../lib
|
||||
|
||||
|
||||
OBJ = ProcessConfig.o Database.o RcdbInfo.o \
|
||||
SocketApiClient.o ServiceInfo.o ContentSelect.o ../ftsd/FtsdSocketControl.o \
|
||||
WorkerSizeHash.o \
|
||||
Main.o
|
||||
|
||||
|
||||
LIBS = ../lib/libInterCommon.a -lpthread -lrt /user/db/pgsql/lib/libpq.a
|
||||
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
|
||||
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 ../ftsd/*.o ../ftsd/*.out ../ftsd/core.*
|
||||
-rm -f $(APP)
|
||||
sync
|
||||
|
||||
|
||||
install : $(APP)
|
||||
-cp $(PROG_NAME) $(INSTALL_BIN)/$(PROG_NAME)
|
||||
sync
|
||||
|
||||
|
||||
# End of Makefile
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
// static 변수 초기화
|
||||
CProcessConfig * CProcessConfig::m_pInstance = NULL;
|
||||
|
||||
|
||||
// 생성자
|
||||
CProcessConfig::CProcessConfig()
|
||||
{
|
||||
// default 값 설정.
|
||||
|
||||
// FHS File Transfer Daemon (ftsd) TCP Listen Port = 14001 (default)
|
||||
m_nFhsTransferDaemonPort = 14001;
|
||||
|
||||
// content size, MD5 hash info request listen port = 14021 (default)
|
||||
m_nTcpListenPortSizeHash = 14021;
|
||||
}
|
||||
|
||||
// 소멸자
|
||||
CProcessConfig::~CProcessConfig()
|
||||
{
|
||||
// 객체 소멸시...
|
||||
// 만약 static GetInstance() 함수가 가르키는 객체가 자기 자신이라면...
|
||||
// 소멸 처리에 의해 문제가 발생할 수 있으므로... 이에 대한 처리를 해 준다.
|
||||
if( CProcessConfig::GetInstance() == this )
|
||||
{
|
||||
CProcessConfig::m_pInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CProcessConfig::Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
|
||||
{
|
||||
|
||||
// 1. 임시 처리용 Instance 를 생성한다.
|
||||
CProcessConfig * pTempInstance = new CProcessConfig();
|
||||
|
||||
// 2. 전달 받은 Config 정보를 임시 Instance 로 로딩한다.
|
||||
// - 만약 로딩이 실패할 경우... 임시 Instance 객체를 삭제 처리한다.
|
||||
if( pTempInstance->Load( strProgramName, strConfFileName, strErrorMessage ) == false )
|
||||
{
|
||||
delete pTempInstance;
|
||||
pTempInstance = NULL;
|
||||
|
||||
return false; // 생성 실패 사유는 Error String 관련 함수를 사용하여 확인.
|
||||
}
|
||||
|
||||
// 3. 전달받은 Conf 정보 로딩에 성공한 경우...
|
||||
// - 기존 Instance 객체가 존재하는 경우.. 교체 처리..
|
||||
// - 기존 Instance 객체가 없는 경우는 신규 Instance 사용토록 처리.
|
||||
if( CProcessConfig::GetInstance() == NULL )
|
||||
{
|
||||
CProcessConfig::m_pInstance = pTempInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Instance replace...
|
||||
CProcessConfig * pPreviosInstance = CProcessConfig::m_pInstance;
|
||||
CProcessConfig::m_pInstance = pTempInstance;
|
||||
|
||||
delete pPreviosInstance;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CProcessConfig * CProcessConfig::GetInstance()
|
||||
{
|
||||
return CProcessConfig::m_pInstance;
|
||||
}
|
||||
|
||||
|
||||
bool CProcessConfig::Load( const std::string & strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
|
||||
{
|
||||
// 변수 유효성 확인
|
||||
if( strProgramName.size() <= 0 )
|
||||
{
|
||||
strErrorMessage = "Config Program Name[" + strProgramName + "] not valid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Config class (lib 하위) 를 이용하여 config 파일 상의 정보를 로딩 처리
|
||||
Config conf;
|
||||
|
||||
if( conf.Open( strConfFileName ) == false )
|
||||
{
|
||||
strErrorMessage = "Config file[" + strConfFileName + "] open failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Config 모듈에 로딩된 정보 중 필요한 정보만 가져온다...
|
||||
// - 만약 오류가 발생할 경우... 해당 내역은 Error String 에 저장 처리...
|
||||
|
||||
std::string strValue;
|
||||
//std::vector< std::string > vecValue;
|
||||
|
||||
|
||||
// DEFAULT_LOG_DIR
|
||||
if( GetConfigValue( conf, strProgramName, "DEFAULT_LOG_DIR", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strLogPath = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// LOG_LEVEL
|
||||
if( GetConfigValue( conf, strProgramName, "LOG_LEVEL", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_nLogLevel = atoi(strValue.c_str());
|
||||
if( m_nLogLevel < 0 || m_nLogLevel > MAX_LOG_LEVEL )
|
||||
{
|
||||
strErrorMessage = "Config [LOG_LEVEL] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCID
|
||||
if( GetConfigValue( conf, strProgramName, "RC_ID", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcId = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB IP
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_IP", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcdbIp = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB PORT
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_PORT", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_nRcdbPort = atoi( strValue.c_str() );
|
||||
|
||||
if( m_nRcdbPort <= 0 || m_nRcdbPort > 65535 )
|
||||
{
|
||||
strErrorMessage = "Config [RCDB_PORT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB DB NAME
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_DB_NAME", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcdbName = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB ACCOUNT
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcdbAcct = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB ACCOUNT PASSWORD
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT_PW", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcdbAcctPw = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
|
||||
// FHS_TRANSFER_DAEMON_PORT
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
if( GetConfigValue(conf, strProgramName, "FHS_TRANSFER_DAEMON_PORT", strValue, strErrorMessage) == true )
|
||||
{
|
||||
m_nFhsTransferDaemonPort = atoi(strValue.c_str());
|
||||
|
||||
if( m_nFhsTransferDaemonPort <= 0 || m_nFhsTransferDaemonPort > 65535 )
|
||||
{
|
||||
strErrorMessage = "Config [FHS_TRANSFER_DAEMON_PORT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// PORT_TCP_LISTEN_SIZE_HASH
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
if( GetConfigValue(conf, strProgramName, "PORT_TCP_LISTEN_SIZE_HASH", strValue, strErrorMessage) == true )
|
||||
{
|
||||
m_nTcpListenPortSizeHash = atoi(strValue.c_str());
|
||||
|
||||
if( m_nTcpListenPortSizeHash <= 0 || m_nTcpListenPortSizeHash > 65535 )
|
||||
{
|
||||
strErrorMessage = "Config [PORT_TCP_LISTEN_SIZE_HASH] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// conf 정보 로딩이 모두 정상적으로 완료된 경우... 마지막으로 전달받은 config 명, section 정보를 저장한다.
|
||||
m_strConfigFileName = strConfFileName;
|
||||
m_strProgramName = strProgramName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CProcessConfig::GetConfigValue( Config & conf, const std::string & strProgramName, const std::string strKey, std::string & strValue, std::string & strErrorMessage )
|
||||
{
|
||||
// conf get value
|
||||
if( conf.GetConfig( strProgramName, strKey, strValue) == false )
|
||||
{
|
||||
if( conf.GetConfig( "COMMON", strKey, strValue) == false )
|
||||
{
|
||||
strErrorMessage = "Config [" + strKey + "] value is not exist";
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// value check.
|
||||
if( strValue.empty() == true )
|
||||
{
|
||||
strErrorMessage = "Config [" + strKey + "] value is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/****************************************************************************
|
||||
rc_apid config processing module
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2021/03/09
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __PROCESS_CONFIG_H__
|
||||
#define __PROCESS_CONFIG_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
|
||||
// 본 모듈은 conf 파일의 정보를 read 하여... 본 프로세스 관련 설정 정보를 저장한다.
|
||||
// 본 모듈은 singleton 으로 동작시켜 Main 프로세스 및 Worker 프로세스에서도 접근이 가능토록 한다.
|
||||
// conf 파일에 대한 파싱 및 설정 정보 추출은 lib 하위의 Config 클래스를 이용한다.
|
||||
// 본 객체는 singleton 객체이므로 상속받아 사용하지 않도록 한다. ( 상속받아 사용할 수 없도록 private 처리함 )
|
||||
class CProcessConfig
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
// sigleton 객체 생성을 위해 생성자를 public 으로 처리하지 않음.
|
||||
CProcessConfig();
|
||||
|
||||
// 소멸자
|
||||
virtual ~CProcessConfig();
|
||||
|
||||
static CProcessConfig * m_pInstance;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// 객체 초기화 처리용 Function.
|
||||
// 본 함수 호출시 Instance 생성 후 입력 받은 Config 파일 load.
|
||||
// 따라서 본 함수를 호출하지 않을 경우.. GetInstance() 함수는 NULL 을 반환
|
||||
// 본 함수를 객체가 생성된 상태에서 재 호출할 경우
|
||||
// - 주어진 Conf 파일 정보가 정확하다면.. conf 정보를 다시 load 함.
|
||||
// - 주어진 conf 파일 정보가 부정확하다면... 이전 conf 를 계속 사용함.
|
||||
static bool Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage );
|
||||
|
||||
// Singleton 객체 접근 메소드
|
||||
static CProcessConfig * GetInstance();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// 전달받은 conf 파일에서 해당 config 정보를 로드하여 멤버 변수에 저장한다.
|
||||
// 만약 반환값이 false 인 경우 해당 오류 관련 내역은 strErrorMessage 변수에 저장된다.
|
||||
bool Load( const std::string & strProgramName, const std::string & strConfFileName, std::string & strErrorMessage );
|
||||
|
||||
|
||||
// Config 객체로 부터 지정된 strKey 정보를 가져와 유효성 여부를 판단.
|
||||
// 반환값이 false 인 경우 strErrorMessage 상에 오류 내역이 저장된다.
|
||||
bool GetConfigValue( Config & conf, const std::string & strProgramName, const std::string strKey, std::string & strValue, std::string & strErrorMessage );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// 객체 초기화시 전달받은 정보
|
||||
std::string m_strConfigFileName; // 설정 파일 정보
|
||||
std::string m_strProgramName; // 프로그램 명 (conf 상에서 해당 프로그램의 설정 정보를 가져올 때 사용)
|
||||
|
||||
// 기본 설정 정보.
|
||||
std::string m_strLogPath; // 로그 저장 Path (/user/service/logs)
|
||||
int m_nLogLevel; // 로그 기록 Level
|
||||
|
||||
std::string m_strRcId; // RC ID
|
||||
|
||||
std::string m_strRcdbIp; // RCDB IP
|
||||
unsigned int m_nRcdbPort; // RCDB Port
|
||||
std::string m_strRcdbName; // RCDB DB Name
|
||||
std::string m_strRcdbAcct; // RCDB 접근 계정
|
||||
std::string m_strRcdbAcctPw; // RCDB 접근 Password.
|
||||
|
||||
|
||||
// 프로세스 설정 정보
|
||||
|
||||
unsigned int m_nFhsTransferDaemonPort; // FHS File Transfer Daemon (ftsd) TCP Listen Port = 14001 (default)
|
||||
|
||||
unsigned int m_nTcpListenPortSizeHash; // content size, MD5 hash info request listen port = 14021 (default)
|
||||
|
||||
|
||||
|
||||
public: // 멤버 변수에 대한 Get 함수 선언 및 정의
|
||||
|
||||
const char * GetConfigFileName() { return m_strConfigFileName.c_str(); }
|
||||
const char * GetProgramName() { return m_strProgramName.c_str(); }
|
||||
|
||||
const char * GetLogPath() { return m_strLogPath.c_str(); }
|
||||
int GetLogLevel() { return m_nLogLevel; }
|
||||
|
||||
const char * GetRcId() { return m_strRcId.c_str(); }
|
||||
|
||||
const char * GetRcdbIp() { return m_strRcdbIp.c_str(); }
|
||||
unsigned int GetRcdbPort() { return m_nRcdbPort; }
|
||||
const char * GetRcdbName() { return m_strRcdbName.c_str(); }
|
||||
const char * GetRcdbAcct() { return m_strRcdbAcct.c_str(); }
|
||||
const char * GetRcdbAcctPw() { return m_strRcdbAcctPw.c_str(); }
|
||||
|
||||
unsigned int GetFhsTransferDaemonPort() { return m_nFhsTransferDaemonPort; }
|
||||
unsigned int GetSizeHashListenPort() { return m_nTcpListenPortSizeHash; }
|
||||
|
||||
};
|
||||
|
||||
#endif /* __PROCESS_CONFIG_H__ */
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "RcdbInfo.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
// 생성자..
|
||||
CRcdbInfo::CRcdbInfo()
|
||||
{
|
||||
}
|
||||
|
||||
// 소멸자..
|
||||
CRcdbInfo::~CRcdbInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// CServiceConfig Class 에 저장된 RCDB 접속 정보를 가져와 멤버 변수에 저장한다.
|
||||
void CRcdbInfo::Load()
|
||||
{
|
||||
// CProcessConfig Class 에서 기본적인 설정값에 대해 검사를 수행하므로...
|
||||
// 본 Class 에서 다시 유효성 검사를 할 필요는 없다.. ( 어차피 유효성 검사 방법이 동일하므로.. )
|
||||
|
||||
// RCDB IP
|
||||
m_strRcdbIp = CProcessConfig::GetInstance()->GetRcdbIp();
|
||||
|
||||
// RCDB Port
|
||||
m_nRcdbPort = CProcessConfig::GetInstance()->GetRcdbPort();
|
||||
|
||||
// RCDB DB Name
|
||||
m_strRcdbName = CProcessConfig::GetInstance()->GetRcdbName();
|
||||
|
||||
// RCDB 접근 계정
|
||||
m_strRcdbAcct = CProcessConfig::GetInstance()->GetRcdbAcct();
|
||||
|
||||
// RCDB 접근 Password.
|
||||
m_strRcdbAcctPw = CProcessConfig::GetInstance()->GetRcdbAcctPw();
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
RCDB 접속 정보 저장 처리 Class
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2013/11/13
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage Team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __RCDB_INFO_H__
|
||||
#define __RCDB_INFO_H__
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
|
||||
|
||||
/// @brief CRcdbInfo
|
||||
/// 본 클래스는 conf 파일에 저장된 RCDB 접속 관련 정보를 저장하기 위한 공용 Class...
|
||||
/// 본 클래스를 사용하지 않고... CServiceConfig class 의 Get 함수를 이용하여 RCDB 접속 정보를 처리해도 된다..
|
||||
/// 하지만... RCDB 접속 처리 Class 함수의 string type 변수 사용으로 인해... 여러 모듈에서 반복적인 동일 코드 처리 작업을 수행해야 하므로...
|
||||
/// 본 Class 를 통해 코드를 단순화 시킬 목적으로 사용한다.
|
||||
class CRcdbInfo
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성 및 소멸자.
|
||||
CRcdbInfo();
|
||||
~CRcdbInfo();
|
||||
|
||||
// CServiceConfig class 에 저장된 RCDB 접속 정보를 멤버 변수에 저장처리한다.
|
||||
void Load( void );
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// 멤버 변수들은 DB 접속을 처리하는 객체에서 직접 Access 해서 사용할 수 있도록 public 으로 구성. ( 편의성 제공 목적)
|
||||
std::string m_strRcdbIp; // RCDB IP
|
||||
unsigned int m_nRcdbPort; // RCDB Port
|
||||
std::string m_strRcdbName; // RCDB DB Name
|
||||
std::string m_strRcdbAcct; // RCDB 접근 계정
|
||||
std::string m_strRcdbAcctPw; // RCDB 접근 Password.
|
||||
|
||||
};
|
||||
|
||||
#endif /* __RCDB_INFO_H__ */
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "ServiceInfo.h"
|
||||
|
||||
|
||||
CServiceInfo::CServiceInfo()
|
||||
{
|
||||
// thread handle 초기화
|
||||
m_threadHandle = 0;
|
||||
|
||||
|
||||
}
|
||||
|
||||
CServiceInfo::~CServiceInfo()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
|
||||
// thread 종료 잠시 대기
|
||||
sleep( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
bool CServiceInfo::Start( void )
|
||||
{
|
||||
// 현재는 DB 접속 후 cache 작업을 수행하지 않으므로.. 무조건 true 반환.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CServiceInfo::GetServiceInfo( const std::string & szService, std::string & szServiceSeq, bool & bUriIgnoreCase )
|
||||
{
|
||||
if( szService.compare( "estsoftstg" ) == 0 )
|
||||
{
|
||||
// estsoftstg 서비스인 경우..
|
||||
szServiceSeq = "600";
|
||||
bUriIgnoreCase = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/****************************************************************************
|
||||
rc_apid header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2021/03/14
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __SERVICE_INFO_H__
|
||||
#define __SERVICE_INFO_H__
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
// Service 정보를 cache 해 놓는 class
|
||||
// - RCDB 에서 service 정보를 주기적으로 가져와 저장해 놓기 위한 class
|
||||
// - 당장 사용 빈도가 높지 않아... 이스트소프트는 하드코딩 해 놓음.
|
||||
// - 향후 확장 대비하여 클래스 사전 분리.
|
||||
class CServiceInfo
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// 생성자.
|
||||
CServiceInfo();
|
||||
|
||||
// 소멸자
|
||||
~CServiceInfo();
|
||||
|
||||
|
||||
// 내부 job thread 기동
|
||||
bool Start( void );
|
||||
|
||||
|
||||
// 서비스 정보 전달
|
||||
// - [IN ] szService : service id
|
||||
// - [OUT] szServiceSeq : Service sequence number
|
||||
// - [OUT] bUriIgnoreCase : 대소문자 무시 여부, true: 대소문자 무시, false: 대소문자 구분
|
||||
bool GetServiceInfo( const std::string & szService, std::string & szServiceSeq, bool & bUriIgnoreCase );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __SERVICE_INFO_H__ */
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
#include "SocketApiClient.h"
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
||||
// request 의 body 부분 수신 처리를 담당할 임시 버퍼 크기
|
||||
#define DEFAULT_SOCKET_TEMP_BUFFER_SIZE (1024 * 6)
|
||||
|
||||
|
||||
CSocketApiClient::CSocketApiClient( int clientSocket )
|
||||
: CBaseSocket(clientSocket)
|
||||
, m_iRequestHeaderLen( sizeof( m_stRequestHeader ) )
|
||||
, m_uRequestDataLen(0)
|
||||
{
|
||||
// thread id 정보 추출
|
||||
m_lThreadId = syscall( SYS_gettid );
|
||||
|
||||
}
|
||||
|
||||
CSocketApiClient::~CSocketApiClient()
|
||||
{
|
||||
// Socket 에 대한 명시적 close 처리
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
int CSocketApiClient::GetRequestHeader( int timeout )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return -1;
|
||||
|
||||
// Socket 으로 부터 Request Header 부분 수신.
|
||||
// 수신된 정보는 멤버변수에 저장처리.
|
||||
/// read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
|
||||
int nRead = ReadNTimeout( &m_stRequestHeader, m_iRequestHeaderLen, timeout );
|
||||
if( nRead == -2 )
|
||||
{
|
||||
// timeout 발생시
|
||||
return 0;
|
||||
}
|
||||
else if( nRead <= 0 )
|
||||
{
|
||||
// timeout 을 제외한 그 밖의 오류 발생시
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 정상 Data 수신시.
|
||||
|
||||
// STX code 검사.
|
||||
if( m_stRequestHeader.id != HEADER_ID_CODE_API_CLIENT )
|
||||
{
|
||||
_LOG( LERR, "[SocketApiClient %d %ld] not valid request header id.", getpid(), m_lThreadId );
|
||||
PrintHeaderToLog();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Data Length 부분 값을 멤버 변수에 저장처리.
|
||||
m_uRequestDataLen = ntohl( m_stRequestHeader.data_length );
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CSocketApiClient::PrintHeaderToLog( void )
|
||||
{
|
||||
_LOG( LINF, "------------------------------------" );
|
||||
_LOG( LINF, "id [%u]", ntohl( m_stRequestHeader.id) );
|
||||
_LOG( LINF, "data_length [%u]", m_uRequestDataLen );
|
||||
_LOG( LINF, "------------------------------------" );
|
||||
}
|
||||
|
||||
int CSocketApiClient::GetRequestSizHashBody( std::string & szService, std::string & szUri, std::string & szErrorMessage )
|
||||
{
|
||||
char tempBuffer[DEFAULT_SOCKET_TEMP_BUFFER_SIZE];
|
||||
|
||||
// Body 크기가 buffer 보다 큰 경우...
|
||||
if( m_uRequestDataLen > DEFAULT_SOCKET_TEMP_BUFFER_SIZE )
|
||||
{
|
||||
sprintf( tempBuffer, "request body size too large. request[%u] buffer[%u]"
|
||||
, m_uRequestDataLen, DEFAULT_SOCKET_TEMP_BUFFER_SIZE );
|
||||
|
||||
szErrorMessage = tempBuffer;
|
||||
|
||||
_LOG( LERR, "[SocketApiClient %d %ld] request body size too large. request[%u] socket_buffer[%u]"
|
||||
, getpid(), m_lThreadId
|
||||
, m_uRequestDataLen, DEFAULT_SOCKET_TEMP_BUFFER_SIZE );
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// body 수신작업 전 tempBuffer 초기화.
|
||||
memset( &tempBuffer, 0x00, DEFAULT_SOCKET_TEMP_BUFFER_SIZE );
|
||||
|
||||
int nRead = 0;
|
||||
nRead = ReadNTimeout( &tempBuffer, m_uRequestDataLen, 3);
|
||||
if( nRead == -2 )
|
||||
{
|
||||
// timeout 발생시
|
||||
sprintf( tempBuffer, "request body[%u] recv timeout(3 sec).", m_uRequestDataLen );
|
||||
szErrorMessage = tempBuffer;
|
||||
|
||||
_LOG( LERR, "[SocketApiClient %d %ld] request body[%u] recv timeout(3 sec)."
|
||||
, getpid(), m_lThreadId, m_uRequestDataLen);
|
||||
|
||||
return -1;
|
||||
}
|
||||
else if( nRead <= 0 )
|
||||
{
|
||||
// timeout 을 제외한 그 밖의 오류 발생시
|
||||
szErrorMessage = "request body recv error.";
|
||||
|
||||
_LOG( LERR, "[SocketApiClient %d %ld] request body[%u] recv error"
|
||||
, getpid(), m_lThreadId, m_uRequestDataLen);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// body 정보가 정상 수신된 경우.
|
||||
_LOG( LDBG, "[SocketApiClient %d %ld] request body [%s]", getpid(), m_lThreadId, tempBuffer );
|
||||
|
||||
// json 형식의 body 정보를 파싱 처리한다.
|
||||
// 파싱 편의를 위해 전달받은 data 를 string 처리
|
||||
std::string szBody = tempBuffer;
|
||||
std::string::size_type posStart;
|
||||
std::string::size_type posEnd;
|
||||
|
||||
// 서비스 정보 추출
|
||||
posStart = szBody.find( "\"service\":\"" );
|
||||
if( posStart == std::string::npos )
|
||||
{
|
||||
szErrorMessage = "request parsing error. service key not exist.";
|
||||
|
||||
// service 정보가 없는 경우...
|
||||
_LOG( LERR, "[SocketApiClient %d %ld] request parsing error. service key not exist.[%s]"
|
||||
, getpid(), m_lThreadId, szBody.c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
posStart += 11;
|
||||
posEnd = szBody.find( '"', posStart);
|
||||
szService = szBody.substr( posStart, posEnd - posStart );
|
||||
|
||||
// uri 정보 추출
|
||||
posStart = szBody.find( "\"uri\":\"" );
|
||||
if( posStart == std::string::npos )
|
||||
{
|
||||
szErrorMessage = "request parsing error. uri key not exist.";
|
||||
|
||||
// service 정보가 없는 경우...
|
||||
_LOG( LERR, "[SocketApiClient %d %ld] request parsing error. uri key not exist.[%s]"
|
||||
, getpid(), m_lThreadId, szBody.c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
posStart += 7;
|
||||
posEnd = szBody.find( '"', posStart);
|
||||
szUri = szBody.substr( posStart, posEnd - posStart);
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool CSocketApiClient::SendResultSizeHashError( std::string & szService, std::string & szUri, std::string & szMessage )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return false;
|
||||
|
||||
// 정보 전달을 위한 response header 생성
|
||||
// - request header 와 동일한 형식
|
||||
struct RequestHeaderApiClient stHeader;
|
||||
memset( &stHeader, 0x00, sizeof( struct RequestHeaderApiClient ) );
|
||||
|
||||
stHeader.id = HEADER_ID_CODE_API_CLIENT;
|
||||
|
||||
// response body 정보 생성
|
||||
std::string szBody;
|
||||
|
||||
szBody = "{\"service\":\"" + szService
|
||||
+ "\", \"uri\":\"" + szUri
|
||||
+ "\", \"result\":\"error\", \"size\":\"0\", \"md5\":\"0\", \"msg\":\"" + szMessage
|
||||
+"\"}";
|
||||
|
||||
stHeader.data_length = htonl( szBody.size() );
|
||||
|
||||
// 로그
|
||||
_LOG( LINF, "[SocketApiClient %d %ld] error response [%u][%u][%s]"
|
||||
, getpid(), m_lThreadId
|
||||
, ntohl( stHeader.id ), ntohl( stHeader.data_length )
|
||||
, szBody.c_str() );
|
||||
|
||||
// response header send
|
||||
if( WriteN( &stHeader, m_iRequestHeaderLen ) != m_iRequestHeaderLen )
|
||||
{
|
||||
_LOG( LERR, "[SocketApiClient %d %ld] size, hash error response send fail."
|
||||
, getpid(), m_lThreadId );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// response body 부분 전송
|
||||
// - 뭐 전송 실패해도 할게 없으므로.. 그냥 call 처리.
|
||||
WriteN( szBody.c_str(), szBody.size() );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSocketApiClient::SendResultSizeHash( std::string & szService, std::string & szUri, unsigned long long & uContentLength, std::string & szHash )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return false;
|
||||
|
||||
// 정보 전달을 위한 response header 생성
|
||||
// - request header 와 동일한 형식
|
||||
struct RequestHeaderApiClient stHeader;
|
||||
memset( &stHeader, 0x00, sizeof( struct RequestHeaderApiClient ) );
|
||||
|
||||
stHeader.id = HEADER_ID_CODE_API_CLIENT;
|
||||
|
||||
// 문자열 조립이 쉽도록 .. 숫자를 문자열로 변환 처리 저장.
|
||||
std::stringstream szContentLength;
|
||||
szContentLength << uContentLength;
|
||||
|
||||
// response body 정보 생성
|
||||
std::string szBody;
|
||||
|
||||
szBody = "{\"service\":\"" + szService
|
||||
+ "\", \"uri\":\"" + szUri
|
||||
+ "\", \"result\":\"ok\", \"size\":\"" + szContentLength.str()
|
||||
+ "\", \"md5\":\"" + szHash
|
||||
+ "\", \"msg\":\"\"}";
|
||||
|
||||
stHeader.data_length = htonl( szBody.size() );
|
||||
|
||||
// 로그
|
||||
_LOG( LINF, "[SocketApiClient %d %ld] response [%u][%u][%s]"
|
||||
, getpid(), m_lThreadId
|
||||
, ntohl(stHeader.id), ntohl(stHeader.data_length)
|
||||
, szBody.c_str());
|
||||
|
||||
// response header send
|
||||
if( WriteN( &stHeader, m_iRequestHeaderLen ) != m_iRequestHeaderLen )
|
||||
{
|
||||
_LOG( LERR, "[SocketApiClient %d %ld] size, hash result response send fail."
|
||||
, getpid(), m_lThreadId );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// response body 부분 전송
|
||||
// - 뭐 전송 실패해도 할게 없으므로.. 그냥 call 처리.
|
||||
WriteN( szBody.c_str(), szBody.size() );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/****************************************************************************
|
||||
rc_apid header for API Server 와 통신을 담당하는 socket class
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2021/03/11
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __SOCKET_API_CLIENT_H__
|
||||
#define __SOCKET_API_CLIENT_H__
|
||||
|
||||
#include "BaseSocket.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// API server Client 와 통신 관련 id 정보 ( 숫자로 9 이며.. network byte order 변환시.. 0x09000000 임)
|
||||
#define HEADER_ID_CODE_API_CLIENT 0x09000000
|
||||
|
||||
|
||||
// API Server client 와 TCP 통신 관련 request header 구조체
|
||||
struct RequestHeaderApiClient {
|
||||
|
||||
unsigned int id; // packet 유효성 관리 코드 (Nwtwork Byte order)
|
||||
unsigned int data_length; // packet data 부분의 길이값 (Network Byte order)
|
||||
};
|
||||
|
||||
|
||||
// API Server 와 TCP 통신 및 Data 송,수신 처리를 담당할 class
|
||||
class CSocketApiClient : public CBaseSocket
|
||||
{
|
||||
|
||||
public:
|
||||
// 생성자
|
||||
// param [in] clientSocket API Server 와 연결된 socket.
|
||||
CSocketApiClient( int clientSocket );
|
||||
|
||||
// 소멸자
|
||||
~CSocketApiClient();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// API Server client 와 통신 관련 request header
|
||||
struct RequestHeaderApiClient m_stRequestHeader;
|
||||
|
||||
// m_stRequestHeader 구조체의 크기를 저장하기 위한 상수
|
||||
const int m_iRequestHeaderLen;
|
||||
|
||||
// Packet Hader 에 저장된 data 부분의 길이 정보값
|
||||
unsigned int m_uRequestDataLen;
|
||||
|
||||
|
||||
// log 기록시 사용할 thread id 정보
|
||||
long m_lThreadId;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Request Header 부분의 수신 처리를 위한 함수
|
||||
// param timeout [in] 대기시간.
|
||||
// retrun 0 : timeout
|
||||
// 1 : 성공
|
||||
// -1 : socket 오류 또는 정의된 Data 가 아닌 경우.
|
||||
int GetRequestHeader( int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
|
||||
|
||||
// Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
|
||||
void PrintHeaderToLog( void );
|
||||
|
||||
// Request size, hash 정보 추출 요청 관련 body 정보 수신 처리
|
||||
// retrun 1 : 성공
|
||||
// 0 : body 정보 파싱 오류 발생시
|
||||
// -1 : socket 오류 또는 정의된 Data 가 아닌 경우.
|
||||
int GetRequestSizHashBody( std::string & szService, std::string & szUri, std::string& szErrorMessage );
|
||||
|
||||
|
||||
// size, hash 요청 관련 오류 발생 결과를 client 에게 전송
|
||||
bool SendResultSizeHashError( std::string & szService, std::string & szUri, std::string & szMessage );
|
||||
|
||||
// size, hash 요청 관련 최종 처리 발생 결과를 client 에게 전송
|
||||
bool SendResultSizeHash( std::string & szService, std::string & szUri, unsigned long long & uContentLength, std::string & szHash );
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __SOCKET_API_CLIENT_H__ */
|
||||
|
||||
@@ -0,0 +1,681 @@
|
||||
#include "WorkerSizeHash.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessRename.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include "SocketApiClient.h"
|
||||
#include "ServiceInfo.h"
|
||||
#include "ContentSelect.h"
|
||||
#include "../ftsd/FtsdSocketControl.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
|
||||
// client ip 주소 저장을 위한 최대 크기값
|
||||
// - NULL 포함 40자까지, IPv6 지원 목적
|
||||
#define IP_ADDR_LENGTH 40
|
||||
|
||||
|
||||
// client 요청 수신 관련 tiemout 최대값
|
||||
#define MAX_REQUEST_TIMEOUT 15
|
||||
|
||||
|
||||
// 고객사에서 요청하는 Content size, md5 hash 정보 처리를 위한 TCP listen socket
|
||||
// Main.cpp 에 전역변수로 정의 되어 있다.
|
||||
extern int g_listenSocketSizeHash;
|
||||
|
||||
// ServiceInfo 전역객체
|
||||
// - service 정보를 caching 하는 객체는 각 client thread 에서 사용해야 하므로.. 전역 객체로 생성한다.
|
||||
CServiceInfo g_objServiceInfo;
|
||||
|
||||
|
||||
// 각 Client 를 처리할 Thread 에 전달할 구조체
|
||||
struct stSizeHashThreadArg {
|
||||
|
||||
// client socket descriptor
|
||||
int clientSocket;
|
||||
|
||||
// Client 의 IP 주소 정보를 저장한 변수
|
||||
char szClientAddress[IP_ADDR_LENGTH];
|
||||
};
|
||||
|
||||
|
||||
// thread id 정보 반환 함수.
|
||||
long GetThreadId()
|
||||
{
|
||||
//return syscall( __NR_gettid );
|
||||
return syscall( SYS_gettid );
|
||||
}
|
||||
|
||||
|
||||
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
|
||||
void ReadyToExitWorkerSizeHash()
|
||||
{
|
||||
// open 된 socket 관련 Close 처리
|
||||
if( g_listenSocketSizeHash != -1 )
|
||||
close( g_listenSocketSizeHash );
|
||||
|
||||
// Logger 객체 종료 처리.
|
||||
CLogger::Exit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Worker SizeHash 프로세스에 대한 대한 종료 처리 수신시.
|
||||
static void SignalWorkerSizeHashTerminate( int nSignalNumber )
|
||||
{
|
||||
// Signal Number 에 따른 로깅처리.
|
||||
if( nSignalNumber == SIGTERM )
|
||||
{
|
||||
_LOG( LINF, "[SizeHash %d] Process exit by SIGTERM signal. Good Bye..", getpid() );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[SizeHash %d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
|
||||
}
|
||||
|
||||
ReadyToExitWorkerSizeHash();
|
||||
|
||||
// 종료전 잠시 대기
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
// Worker::SizeHash Process 의 signal 처리기...
|
||||
void SetWorkerSizeHashSignalHandler()
|
||||
{
|
||||
sigset_t set;
|
||||
struct sigaction act;
|
||||
|
||||
memset( &act, 0x00, sizeof( act ) );
|
||||
sigfillset( &set );
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
sigfillset( &act.sa_mask );
|
||||
|
||||
// 무시 처리 signal
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction( SIGPIPE, &act, NULL ); /* desciptor 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
|
||||
sigaction( SIGHUP, &act, NULL ); /* process를 기동시킨 관리자의 로그아웃시, 또는 config reload 등의 처리 signal*/
|
||||
sigaction( SIGINT, &act, NULL ); /* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
|
||||
sigaction( SIGQUIT, &act, NULL ); /* 키보드에 의한 Abort 신호 처리 => ? */
|
||||
|
||||
// Worker 프로세스는 자식 프로세스가 존재하지 않으므로 그냥 무시처리함.
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction( SIGCHLD, &act, NULL );
|
||||
|
||||
/* 각종 에러나 사용자의 종료 신호 처리 */
|
||||
act.sa_handler = SignalWorkerSizeHashTerminate;
|
||||
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
|
||||
|
||||
// 나머지 신호는 default 처리.
|
||||
|
||||
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
}
|
||||
|
||||
// Worker::SizeHash 에서 각 Client 에 대한 처리를 수행하기 위한 Thread.
|
||||
void * WorkerSizeHashClientThread( void * arg )
|
||||
{
|
||||
// 전달받은 변수를 내부 변수에 저장 처리.
|
||||
|
||||
// size, hash 요청 관련 client socket 처리를 담당할 객체 생성
|
||||
CSocketApiClient clientSocket( ( (struct stSizeHashThreadArg *)arg )->clientSocket );
|
||||
|
||||
// client 주소 처리 관련
|
||||
char szClientAddress[IP_ADDR_LENGTH];
|
||||
memcpy( szClientAddress, ( (struct stSizeHashThreadArg *)arg )->szClientAddress, IP_ADDR_LENGTH );
|
||||
|
||||
// malloc 로 생성된 변수 해제 처리
|
||||
free( arg );
|
||||
|
||||
// 본 thead 는 client 연결 상태에 따라 종료되므로.. process 에서 분리 처리
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
_LOG( LINF, "[SizeHash %d %ld] client thread start. [%s]", getpid(), GetThreadId(), szClientAddress );
|
||||
|
||||
// 사용 변수 초기화
|
||||
int nResult;
|
||||
|
||||
// 2021-03-12 세션 종료 관련 API Server 담당 이준기 과장 협의 내역
|
||||
// - API Server 에서 request 에 대한 결과 수신 완료 후 세션 종료 처리한다고 함.
|
||||
// - 따라서... 본 모듈은 Server 이므로... 먼저 세션 clsoe 처리하지 않고....
|
||||
// - 1개 세션에 대해서 다중 요청을 처리해 줄 수 있도록 한다.
|
||||
// - 만약 오류 발생 또는 통신규칙 위반시에는 바로 세션 종료 처리한다.
|
||||
|
||||
// socket 수신 대기
|
||||
while( 1 )
|
||||
{
|
||||
|
||||
// request 수신 대기
|
||||
nResult = clientSocket.GetRequestHeader( MAX_REQUEST_TIMEOUT );
|
||||
|
||||
if( nResult == 0 )
|
||||
{
|
||||
// timeout 발생시...
|
||||
// - 지정된 시간동안 요청이 없는 경우 세션을 종료 처리한다.
|
||||
_LOG( LINF, "[SizeHash %d %ld] client[%s] request timeout[%d]. session close."
|
||||
, getpid(), GetThreadId(), szClientAddress, MAX_REQUEST_TIMEOUT );
|
||||
|
||||
// 세션 종료 처리
|
||||
break;
|
||||
}
|
||||
else if( nResult < 0 )
|
||||
{
|
||||
// Socket 종료, 오류 또는 정의되지 않은 형식의 패킷 header 수신시...
|
||||
|
||||
if( clientSocket.IsValidSocket() == false )
|
||||
{
|
||||
// socket 종료시
|
||||
_LOG( LINF, "[SizeHash %d %ld] client[%s] session closed by client."
|
||||
, getpid(), GetThreadId(), szClientAddress );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// socket 처리 오류 또는 정의되지 않은 요청 수신시
|
||||
_LOG( LERR, "[SizeHash %d %lu] client[%s] request recv error. session close."
|
||||
, getpid(), GetThreadId(), szClientAddress );
|
||||
}
|
||||
|
||||
// 세션 종료 처리
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// 정상 request header 수신시.
|
||||
std::string szService;
|
||||
std::string szUri;
|
||||
std::string szErrorMessage;
|
||||
|
||||
// size, hash 정보 추출 요청 관련 body 정보 수신
|
||||
nResult = clientSocket.GetRequestSizHashBody( szService, szUri, szErrorMessage );
|
||||
|
||||
if( nResult == -1 )
|
||||
{
|
||||
// body 정보 수신 실패시. (timeout, length 오류 등)
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] request body recv error[%s]. session close."
|
||||
, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
|
||||
|
||||
// client 에 응답 하지 않고.. 세션을 바로 종료 처리한다.
|
||||
break;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// body 정보가 지정된 규칙의 key 가 존재하지 않아 파싱 오류시
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] request body format error[%s]."
|
||||
, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
|
||||
|
||||
// 파싱 오류가 발생할 상황이므로... 우선 client 로 error message 전송을 시도한다.
|
||||
if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
|
||||
|
||||
}
|
||||
|
||||
// request 관련 통신 규칙 문제이므로...
|
||||
// 세션을 다시 대기시키지 않고 약 1 sec sleep 후 정상 종료 처리 시킨다.
|
||||
sleep( 2 );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// body 정보 정상 수신시...
|
||||
|
||||
_LOG( LINF, "[SizeHash %d %ld] client[%s] request recv ok. service[%s] uri[%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress, szService.c_str(), szUri.c_str() );
|
||||
|
||||
// 추출된 service , uri 정보가 유효한지 check
|
||||
if( szService.empty() == true || szUri.empty() == true )
|
||||
{
|
||||
szErrorMessage = "request info not valid. service[" + szService + "] uri[" + szUri + "]";
|
||||
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] request info not valid. servcie[%s] uri[%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress, szService.c_str(), szUri.c_str() );
|
||||
|
||||
// client 한테 오류 내역 전송 처리 한다.
|
||||
if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
|
||||
}
|
||||
|
||||
// 다시 request 대기
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 결과를 저장할 임시 변수
|
||||
bool bUriIgnoreCase;
|
||||
std::string szServiceSeq;
|
||||
|
||||
// service 에 해당하는 서비스 seq 정보를 가져온다.
|
||||
if( g_objServiceInfo.GetServiceInfo( szService, szServiceSeq, bUriIgnoreCase ) == false )
|
||||
{
|
||||
szErrorMessage = "request service[" + szService + "] info not found.";
|
||||
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] request servcie[%s] info not found."
|
||||
, getpid(), GetThreadId(), szClientAddress, szService.c_str() );
|
||||
|
||||
// client 한테 오류 내역 전송 처리 한다.
|
||||
if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
|
||||
}
|
||||
|
||||
// 다시 request 대기
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 서비스 정보가 정상적으로 존재하는 경우...
|
||||
// RCDB 에서 해당 서비스의 uri 정보를 조회하여.. 물리 파일 정보를 확인한다.
|
||||
CContentSelect objContentSelect;
|
||||
if( objContentSelect.Start() == false )
|
||||
{
|
||||
szErrorMessage = "db connection failed.";
|
||||
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] db select module start failed. [%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
|
||||
|
||||
// client 한테 오류 내역 전송 처리 한다.
|
||||
if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
|
||||
}
|
||||
|
||||
// DB 세션 종료 처리
|
||||
objContentSelect.Exit();
|
||||
|
||||
// 다시 request 대기
|
||||
continue;
|
||||
}
|
||||
|
||||
// RCDB 상에서 content 정보 조회
|
||||
std::vector<struct HostSizeFilenameInfo> vecContentInfo;
|
||||
vecContentInfo.clear();
|
||||
|
||||
if( objContentSelect.GetHostSizeFilename( szServiceSeq, bUriIgnoreCase, szUri, szErrorMessage, vecContentInfo ) == false )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] rcdb select fail. [%s][%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, szUri.c_str(), szErrorMessage.c_str() );
|
||||
|
||||
// client 한테 오류 내역 전송 처리 한다.
|
||||
if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress, szErrorMessage.c_str() );
|
||||
}
|
||||
|
||||
// DB 세션 종료 처리
|
||||
objContentSelect.Exit();
|
||||
|
||||
// 다시 request 대기
|
||||
continue;
|
||||
}
|
||||
|
||||
// RCDB 세션 종료 처리
|
||||
objContentSelect.Exit();
|
||||
|
||||
|
||||
// DB 정보 조회 완료
|
||||
_LOG( LDBG, "[SizeHash %d %ld] client[%s] rcdb select ok. [%s] row_count[%lu]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, szUri.c_str(), vecContentInfo.size() );
|
||||
|
||||
|
||||
// 해당 FHS 의 ftsd 로 hash check 요청을 전송하고... 결과를 대기한다.
|
||||
|
||||
// FHS ftsd 와 통신 처리 담당한 socket 객체 생성
|
||||
CFtsdSocketControl fhs;
|
||||
|
||||
unsigned long long uContentLength = 0;
|
||||
std::string szHash;
|
||||
szHash.clear();
|
||||
|
||||
// Size, MD5 정보 추출 성공 여부
|
||||
bool bExtractSuccess = false;
|
||||
|
||||
// 조회된 FHS 로 content 정보 추출 명령 전달.
|
||||
// - 조회 list 는 origin - replication 순이며...
|
||||
// - origin 접속 실패시.. replication 에서 추출 처리한다.
|
||||
std::vector<struct HostSizeFilenameInfo>::const_iterator it;
|
||||
for( it = vecContentInfo.begin(); it != vecContentInfo.end(); it++ )
|
||||
{
|
||||
if( fhs.ConnectTarget( it->szHost, ( CProcessConfig::GetInstance()->GetFhsTransferDaemonPort() ) ) == false )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs connect fail. [%s][%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, it->szHost.c_str(), szUri.c_str() );
|
||||
|
||||
|
||||
// 다음 FHS 에서 재시도
|
||||
fhs.Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
// FHS 와 정상 접속시...
|
||||
// - 전체 크기에 대한 hash 추출 요청 전달.
|
||||
if( fhs.SendFileCheckRequest( it->szFilename, true, 0 ) == false )
|
||||
{
|
||||
// 해당 FHS 로 요청 전달 실패시..
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs request send fail. [%s][%s][%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str() );
|
||||
|
||||
// 다음 FHS 에서 재시도
|
||||
fhs.Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
_LOG( LINF, "[SizeHash %d %ld] client[%s] fhs hash request send ok. [%s][%s][%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str() );
|
||||
|
||||
|
||||
// 사용 변수 초기화
|
||||
|
||||
int nTimeout = 5; // 응답 대기 시간 ( sec )
|
||||
bool bJobSuccess = false; // 처리 결과의 성공/실패 여부를 저장하기 위한 변수.
|
||||
|
||||
// fhs 로 부터 응답 수신 대기
|
||||
while( 1 )
|
||||
{
|
||||
// nTimeout 에 지정된 시간동안 응답대기 시도
|
||||
// GetFileCheckResult() 함수는 SocketControl.h 파일 참고.
|
||||
nResult = fhs.GetFileCheckResult( nTimeout, bJobSuccess, szErrorMessage, uContentLength, szHash );
|
||||
|
||||
if( nResult == -1 )
|
||||
{
|
||||
// Socket 통신 관련 오류 또는 접속 종료가 발생한 경우.
|
||||
// 해당 내역 로깅 및 루프 종료
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs response wait fail by socket. [%s][%s][%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str() );
|
||||
|
||||
// 다음 FHS 에서 재시도
|
||||
fhs.Close();
|
||||
break;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우 => Allive Check 패킷 한번 쏘고 다시 Loop 로
|
||||
if( fhs.SendAliveCheck() == false )
|
||||
{
|
||||
// Alive 전송 실패시 => Socket 종료 및 오류가 발생한 경우임.
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs response wait2 fail by socket. [%s][%s][%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str() );
|
||||
|
||||
// 다음 FHS 에서 재시도
|
||||
fhs.Close();
|
||||
break;
|
||||
}
|
||||
|
||||
// 정상적인 경우 다시 응답대기.
|
||||
continue;
|
||||
}
|
||||
else if( nResult == 1 )
|
||||
{
|
||||
// 요청에 대한 처리결과 정보가 수신된 경우.
|
||||
if( bJobSuccess == true )
|
||||
{
|
||||
// 요청에 대한 처리가 정상적으로 처리된 경우
|
||||
_LOG( LINF, "[SizeHash %d %ld] client[%s] fhs extract ok. [%s][%s][%s]: [%llu][%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str()
|
||||
, uContentLength, szHash.c_str() );
|
||||
|
||||
|
||||
// 물리 파일 size 와 RCDB size 가 동일한지 check.
|
||||
if( uContentLength != it->uContentLength )
|
||||
{
|
||||
_LOG( LWAR, "[SizeHash %d %ld] client[%s] content size mismatch. [%s][%s][%s]: local[%llu] rcdb[%llu]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str()
|
||||
, uContentLength, it->uContentLength );
|
||||
}
|
||||
|
||||
// 정상 응답을 받았으니.. 정상 처리 flag 설정
|
||||
bExtractSuccess = true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// FHS 로 부터 오류 응답을 수신한 경우..
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] fhs extract error. [%s][%s][%s]: [%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, it->szHost.c_str(), it->szFilename.c_str(), szUri.c_str(), szErrorMessage.c_str() );
|
||||
}
|
||||
|
||||
// 응답을 받았으니 응답 대기 루프 종료
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replication 요청에 대한 응답패킷이 아닌 경우.
|
||||
// 해당 패킷은 무시하고 다시 Loop 로
|
||||
continue;
|
||||
}
|
||||
} // while(1)
|
||||
|
||||
|
||||
// 해당 FHS 에서 정상 응답 수신을 못한 경우.. 다음 장비에서 요청 처리한다.
|
||||
fhs.Close();
|
||||
if( bExtractSuccess == true )
|
||||
{
|
||||
// 정상 응답을 수신한 경우.. 다음 FHS 한테 시도하지 않고.. 종료 처리한다.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 정상 응답을 수신하지 못한 경우.. 다음 장비로 다시 시도.
|
||||
continue;
|
||||
}
|
||||
|
||||
} // for()
|
||||
|
||||
|
||||
// 사용 완료된 변수는 정리
|
||||
vecContentInfo.clear();
|
||||
|
||||
|
||||
// 최종.. API Client 로 결과 전송 처리
|
||||
if( bExtractSuccess == true )
|
||||
{
|
||||
// client 한테 최종 결과 내역 전송 처리 한다.
|
||||
if( clientSocket.SendResultSizeHash( szService, szUri, uContentLength, szHash ) == false )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] response send fail. [%s][%s]: size[%llu] hash[%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, szService.c_str(), szUri.c_str()
|
||||
, uContentLength, szHash.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LINF, "[SizeHash %d %ld] client[%s] response send ok. [%s][%s]: size[%llu] hash[%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, szService.c_str(), szUri.c_str()
|
||||
, uContentLength, szHash.c_str() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// client 한테 오류 내역 전송한다.
|
||||
|
||||
szErrorMessage = "FHS hash extract failed.";
|
||||
|
||||
if( clientSocket.SendResultSizeHashError( szService, szUri, szErrorMessage ) == false )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d %ld] client[%s] error response send fail. [%s][%s]: [%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, szService.c_str(), szUri.c_str(), szErrorMessage.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LINF, "[SizeHash %d %ld] client[%s] error response send ok. [%s][%s]: [%s]"
|
||||
, getpid(), GetThreadId(), szClientAddress
|
||||
, szService.c_str(), szUri.c_str(), szErrorMessage.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
// 다음 request 대기
|
||||
|
||||
} // while(1)
|
||||
|
||||
|
||||
// client 세션 종료 처리.
|
||||
clientSocket.Close();
|
||||
_LOG( LINF, "[SizeHash %d %ld] client[%s] connection close.", getpid(), GetThreadId(), szClientAddress );
|
||||
|
||||
// Thread 종료 관련 처리.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Worker::SizeHash 프로세스의 Main 역활을 수행하기 위한 함수.
|
||||
int WorkerSizeHashMain()
|
||||
{
|
||||
// 전역 변수로 설정된 socket 중 해당 worker 에서 사용하지 않는 socket 은 close 처리한다.
|
||||
|
||||
// Process Rename..
|
||||
set_ps_display( PROG_NAME": SizeHash", false );
|
||||
|
||||
// Set Signal Handler
|
||||
SetWorkerSizeHashSignalHandler();
|
||||
|
||||
// 1. 사용할 객체 초기화 처리
|
||||
|
||||
|
||||
// 2. 각 모듈 기동 처리
|
||||
// - 최종 작업 모듈부터 기동 처리
|
||||
|
||||
if( g_objServiceInfo.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[SizeHash %d] service info thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerSizeHash();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// 3. 수신 처리 관련 변수 정의 및 초기화.
|
||||
int clientSock;
|
||||
struct sockaddr_in clientSockAddr;
|
||||
socklen_t clientSockLen = sizeof( clientSockAddr );
|
||||
|
||||
pthread_t clientThreadId;
|
||||
struct stSizeHashThreadArg * pThreadArg = NULL;
|
||||
|
||||
// log
|
||||
_LOG( LINF, "[SizeHash %d] start waiting for client connection." , getpid());
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
clientSock = accept( g_listenSocketSizeHash, (struct sockaddr *)&clientSockAddr, &clientSockLen );
|
||||
|
||||
if( clientSock == -1 )
|
||||
{
|
||||
// 오류 발생시.... 해당 내역 로깅 처리.
|
||||
int errorNum = errno;
|
||||
_LOG( LERR, "[SizeHash %d] client accept failed [%d][%s]", getpid(), errorNum, strerror( errorNum ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// 정상적인 Client 인 경우...
|
||||
|
||||
// Thread 로 전달할 변수를 malloc 로 생성 처리
|
||||
pThreadArg = (struct stSizeHashThreadArg *)malloc( sizeof( struct stSizeHashThreadArg ) );
|
||||
if( pThreadArg == NULL )
|
||||
{
|
||||
int errorNum = errno;
|
||||
close( clientSock ); // client 접속 종료 처리.
|
||||
|
||||
char tempBuffer[IP_ADDR_LENGTH];
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
if( inet_ntop( AF_INET, &( clientSockAddr.sin_addr ), tempBuffer, sizeof( tempBuffer ) ) != NULL )
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d] client[%s] thread malloc failed [%d][%s]", getpid(), tempBuffer, errorNum, strerror( errorNum ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LERR, "[SizeHash %d] client thread malloc failed [%d][%s] and inet_ntop error[%d][%s]"
|
||||
, getpid(), errorNum, strerror( errorNum ), errno, strerror( errno ) );
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 인자로 전달할 변수정보 설정.
|
||||
pThreadArg->clientSocket = clientSock;
|
||||
|
||||
char tempBuffer[IP_ADDR_LENGTH];
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
if( inet_ntop( AF_INET, &( clientSockAddr.sin_addr ), tempBuffer, sizeof( tempBuffer ) ) != NULL )
|
||||
{
|
||||
memcpy( pThreadArg->szClientAddress, tempBuffer, sizeof( tempBuffer ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// inet_ntop 오류 발생시
|
||||
// ip 가 불분명할 경우.. 관련 상세 내역 로깅 후.. 세션 종료 처리한다.
|
||||
//snprintf( pThreadArg->szClientAddress, IP_ADDR_LENGTH - 1, "0.0.0.0" );
|
||||
|
||||
int errorNum = errno;
|
||||
_LOG( LERR, "[SizeHash %d] client ip not valid. connection closed. inet_ntop error[%d][%s]", getpid(), errorNum, strerror( errorNum ) );
|
||||
|
||||
close( clientSock );
|
||||
free( pThreadArg );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Client 처리를 담당할 Thread 생성 처리
|
||||
if( pthread_create( &clientThreadId, NULL, WorkerSizeHashClientThread, (void *)pThreadArg ) != 0 )
|
||||
{
|
||||
// 생성 실패시...
|
||||
// - 로깅
|
||||
// - client socket close
|
||||
// - malloc 로 생성된 전달 변수 free 처리.
|
||||
|
||||
int errorNum = errno;
|
||||
_LOG( LERR, "[SizeHash %d] client[%s] thread create failed [%d][%s]"
|
||||
, getpid(), pThreadArg->szClientAddress, errorNum, strerror( errorNum ) );
|
||||
|
||||
close( clientSock );
|
||||
free( pThreadArg );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LDBG, "[SizeHash %d] client[%s] accept & client thread create ok."
|
||||
, getpid(), pThreadArg->szClientAddress );
|
||||
}
|
||||
}
|
||||
}
|
||||
} // while(1)
|
||||
|
||||
|
||||
// 종료시 정리 작업 수행
|
||||
ReadyToExitWorkerSizeHash();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/****************************************************************************
|
||||
Worker::SizeHash process main
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2021/03/09
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __WORKER_SIZE_HASH_H__
|
||||
#define __WORKER_SIZE_HASH_H__
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// Worker::SizeHash 프로세스의 Main 역활을 수행하기 위한 함수.
|
||||
int WorkerSizeHashMain();
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __WORKER_SIZE_HASH_H__ */
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
// sample 소스 파일
|
||||
|
||||
#include ""
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
rc_apid header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2021/03/09
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __APID_DATA_H__
|
||||
#define __APID_DATA_H__
|
||||
|
||||
#include <>
|
||||
|
||||
|
||||
|
||||
// class 설명
|
||||
class C
|
||||
{
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __APID_DATA_H__ */
|
||||
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Description : process compile test by huibong
|
||||
#
|
||||
|
||||
|
||||
PROG_NAME=rc_apid
|
||||
|
||||
|
||||
killall $PROG_NAME
|
||||
gmake clean
|
||||
killall $PROG_NAME
|
||||
|
||||
gmake
|
||||
sleep 1
|
||||
./$PROG_NAME -c ../rcts.conf
|
||||
|
||||
sleep 1
|
||||
ps aux | grep $PROG_NAME | grep -v grep | grep -v tail
|
||||
|
||||
Reference in New Issue
Block a user