This commit is contained in:
biosvos
2026-08-07 17:38:18 +09:00
commit 873193a243
9613 changed files with 2755992 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
Revision 1533
-------------------
수정일 : 2021-03-17
수정자 : 유희곤
- NEW: SVN 신규 등록
- 신규 rc_apid 에 대한 SVN 등록
- 본 모듈 관련 자세한 사항 및 설계 내역은 redmine #33479 이슈 참고
-- content 에 대한 size, hash 추출용 worker 를 우선 개발함.
+37
View File
@@ -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
+67
View File
@@ -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
+304
View File
@@ -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__ */
+595
View File
@@ -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;
}
+119
View File
@@ -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__ */
+215
View File
@@ -0,0 +1,215 @@
#include "Config.h"
#include <iostream>
#include <fstream>
#include <errno.h>
#include <stdlib.h>
#include <boost/algorithm/string.hpp> // use boost
Config::Config()
: m_keyDelimiter( CONFIG_DEFAULT_KEY_DELIMITER )
, m_valueDelimiter( CONFIG_DEFAULT_VALUE_DELIMITER )
{
}
/// @brief destructor
Config::~Config()
{
Clear();
}
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
/// @return On success return true, otherwise return false
bool Config::Open( const string path )
{
ifstream file;
// Config file open
file.open( path.c_str());
if( file.is_open() == false )
{
cerr << "[error] " << __FILE__<< ":" << __func__ << ": config file open failed.[" << path << "][" << strerror( errno ) << "]" << endl;
return false;
}
// 기존 데이터가 존재하면 삭제 처리.
if( m_configData.empty() == false )
m_configData.clear();
// config file parsing
string line;
string section;
vector< string > configs;
// 루프를 돌면서 Config 파일을 line 단위로 읽어들인당....
while( std::getline( file, line ) )
{
// 앞뒤 공백 제거 처리
boost::trim( line );
// 공백 or 주석처리 라인 검사
if( line.empty() == true || boost::starts_with( line, string("#") ) == true || line == "\r" )
{
continue;
}
// Section 여부 검사
if( boost::starts_with( line, string("[") ) == true && boost::ends_with( line, string("]") ) == true )
{
// [] 로 정의된 section 의 문자열 값 추출
line.erase( line.begin() );
line.erase( line.end() -1 );
boost::trim( line );
if( section.empty() == false && section != line )
{
// 이전 세션과 다른 신규 세션인 경우
// 기존까지 저장했던 데이터를 멤버 변수에 입력 처리 후 내부변수 초기화 처리.
m_configData.insert( make_pair( section, configs ) );
section.clear();
configs.clear();
}
// 신규 section 정보 저장처리.
section = line;
line.clear();
}
else
{
// Section 정보가 아닌 경우 => 실제 설정값 .. ^^
configs.push_back( line );
}
}
// 마지막 세션 처리된 정보가 존재하는 경우 멤버 변수에 저장 처리.
if( section.empty() == false )
{
m_configData.insert( make_pair( section, configs ) );
}
// 종료 처리.
file.close();
return true;
}
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
/// @return void
void Config::Clear()
{
m_configData.clear();
}
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
/// @return On success return true, otherwise return false
bool Config::GetConfig( const string section, const string key, string& value )
{
// Section 에 대한 임시 데이터 저장객체 생성
vector< string > configs;
// 해당 Section 이 존재하지 않는 경우
if( Find( section, configs ) == false )
return false;
string line;
string result;
vector< string >::const_iterator it;
// 해당 Key 이 존재하는지 검사
for( it = configs.begin(); it != configs.end(); it++)
{
line = *it;
// Line 상의 주석 제거
string::size_type pos = line.find( '#' );
if( pos != string::npos)
{
line = line.substr(0, pos);
boost::trim( line );
}
// key = value 에서 key 부분 추출
pos = line.find( m_keyDelimiter );
if( pos == string::npos )
{
continue;
}
else
{
result = line.substr(0, pos);
boost::trim( result );
}
if( key == result )
{
// Key 값이 동일한 경우
value = line.substr( pos+1 );
boost::trim( value );
return true;
}
}
return false;
}
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
/// @return On success return true, otherwise return false
bool Config::GetConfig( const string section, const string key, vector< string >& value )
{
string result;
vector< string > vec;
if( GetConfig( section, key, result ) == false )
return false;
// 전달받은 result 값을 value delimiter 를 이용하여 parsing 처리
boost::split( vec, result, boost::is_any_of( m_valueDelimiter ));
if( vec.empty() == true )
return false;
// 루프를 돌면서 trim 처리 후 결과값 저장처리.
vector< string >::const_iterator it;
for( it = vec.begin(); it != vec.end(); it++)
{
result = boost::trim_copy( *it );
if( result.empty() == false )
value.push_back( result );
}
if( value.empty() == true )
return false;
else
return true;
}
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
/// @param section [in] section 명
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
/// @return On success return true, otherwise return false
bool Config::Find( const string& section, vector<string>& configData )
{
map< string, vector< string > >::iterator it = m_configData.find( section );
if( it != m_configData.end() )
{
// 해당 section 을 찾은 경우
configData = it->second;
return true;
}
// 해당 section 을 찾지 못한 경우
return false;
}
+114
View File
@@ -0,0 +1,114 @@
/***************************************************************************
Config File Parser Class
-----------------------------------------
begin : 2010/02/27
copyright : (C) 2005 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __LIBRARY_CONFIG_H__
#define __LIBRARY_CONFIG_H__
#include <string>
#include <map>
#include <vector>
using namespace std;
#define CONFIG_DEFAULT_KEY_DELIMITER "="
#define CONFIG_DEFAULT_VALUE_DELIMITER ","
/// @brief Config class
/// 1. config 파일의 내용을 파싱처리하여 각 Section, Key 에 해당하는 값을 반환처리
/// 2. config 파일은 [section] 단위로 구분된다.
/// 3. config 파일은 key=value 로 구분가능하며 이는 Delimiter 설정을 통해 변경가능하다.
/// 4. value 값이 다중으로 존재하는 경우 "," 값을 통해 구분가능, Delimiter 변경시 다른 값도 사용가능함.
/// 5. config 파일상에서 "#" 로 시작하는 문자열은 라인 끝까지 주석으로 처리된다.
/// 6. 특정 Key 값에 대한 다중 value 값 조회는 vector<string> 을 통해 수행한다.
/// 7. 만약 다중 value 값이 존재시 string 으로 반환받을 경우 해당 Row 가 통째로 반환된다.
/// 8. 본 객체는 Open , Clear 함수가 호출되기전까지 이전 Config 정보가 저장된다.
class Config
{
private:
/// @brief config information saved variable
// string : section data
// vector<string> : key=value data
map< string, vector< string> > m_configData;
/// @brief Key, Value 구분자 저장 변수
string m_keyDelimiter;
string m_valueDelimiter;
public:
/// @brief constructor
Config();
/// @brief destructor
~Config();
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
/// @return On success return true, otherwise return false
bool Open( const string path );
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
/// @return void
void Clear();
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
/// @return On success return true, otherwise return false
bool GetConfig( const string section, const string key, string& value );
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
/// @return On success return true, otherwise return false
bool GetConfig( const string section, const string key, vector< string >& value );
/// @brief Key, Value Delimiter Get 함수
/// @param key [out] key delimiter 값
/// @param value [out] value delimiter 값
void GetDelimiter( string& key, string& value )
{
key = m_keyDelimiter;
value = m_valueDelimiter;
}
// BUG 2010-12-06 huibong
// string& value = CONFIG_DEFAULT_VALUE_DELIMITER 값은 문법상 오류 구문임.
// gcc 3.4.6 버전에서는 Compile 되나 gcc 4.4.5 에서는 error 로 처리되어 수정처리함.
/// @brief Key, Value Delimiter Set 함수
/// @param key [out] key delimiter 값
/// @param value [out] value delimiter 값, 지정하지 않을 경우 Default 값이 사용됨.
void SetDelimiter( string& key, string value = CONFIG_DEFAULT_VALUE_DELIMITER )
{
m_keyDelimiter = key;
m_valueDelimiter = value;
}
protected:
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
/// @param section [in] section 명
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
/// @return On success return true, otherwise return false
bool Find( const string& section, vector<string>& configData );
};
#endif /* __LIBRARY_CONFIG_H__ */
+487
View File
@@ -0,0 +1,487 @@
/***************************************************************************
Logger.cpp
-----------------------------------------
begin : 2011/10/26
copyright : (C) 2005 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include "Logger.h"
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <iostream>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define MAX_BUFFER_SIZE 2048 // 임시 버퍼 최대 크기
#define DEFAULT_LOG_LEVEL LINF
using namespace std;
CLogger* CLogger::m_pInstance = NULL;
bool CLogger::m_bIsInitialized = false;
pthread_mutex_t CLogger::m_mutex = PTHREAD_MUTEX_INITIALIZER;
CLogger::CLogger( string programName, string logDir, int logLevel )
{
m_szProgramName = programName;
m_szLogDir = logDir;
if( IsValidLogLevel( logLevel ) == true )
{
m_nLogLevel = logLevel;
}
else
{
m_nLogLevel = DEFAULT_LOG_LEVEL;
}
MakeLogLevelString();
}
void CLogger::MakeLogLevelString()
{
m_vectorLogLevelString.push_back( "EMR" );
m_vectorLogLevelString.push_back( "ALT" );
m_vectorLogLevelString.push_back( "CRT" );
m_vectorLogLevelString.push_back( "ERR" );
m_vectorLogLevelString.push_back( "WAR" );
m_vectorLogLevelString.push_back( "NOT" );
m_vectorLogLevelString.push_back( "INF" );
m_vectorLogLevelString.push_back( "DBG" );
m_vectorLogLevelString.push_back( "DEV" );
m_vectorLogLevelString.push_back( "DEV1" );
m_vectorLogLevelString.push_back( "DEV2" );
}
CLogger::~CLogger()
{
m_vectorLogLevelString.clear();
}
bool CLogger::Init( string programName, string logDir, int logLevel )
{
if( m_bIsInitialized == true )
{
return true;
}
pthread_mutex_lock( &m_mutex );
// 변수 유효성 검사.
if( programName.empty() == true || logDir.empty() == true )
{
pthread_mutex_unlock( &m_mutex );
return false;
}
if( IsValidLogLevel( logLevel ) == false )
{
pthread_mutex_unlock( &m_mutex );
return false;
}
if( MakeLogDir( programName, logDir ) == false )
{
pthread_mutex_unlock( &m_mutex );
return false;
}
if( CLogger::m_pInstance != NULL )
{
delete CLogger::m_pInstance;
CLogger::m_pInstance = NULL;
}
CLogger::m_pInstance = new CLogger( programName, logDir, logLevel );
m_bIsInitialized = true;
pthread_mutex_unlock( &m_mutex );
return true;
}
bool CLogger::IsValidLogLevel( int logLevel )
{
return ( ( logLevel < 0 || logLevel > MAX_LOG_LEVEL ) ? false : true );
}
bool CLogger::SetLogLevel( int logLevel )
{
if( IsValidLogLevel( logLevel ) == false )
{
return false;
}
m_nLogLevel = logLevel;
return true;
}
bool CLogger::MakeLogDir( string programName, string logDir )
{
string path = logDir + "/" + programName;
struct stat dirStat;
// 해당 이름을 가진 파일 또는 디렉토리가 존재하고
if( lstat( path.c_str(), &dirStat ) == 0 )
{
// 해당 이름이 디렉토리인 경우
if( S_ISDIR( dirStat.st_mode ) == true )
{
return true;
}
else
{
cerr << "exist file with the same name as log path(" << path << ")." << endl;
return false;
}
}
// 디렉토리 가 존재하지 않는 경우 디렉토리 생성 시도
string cmd = "mkdir -p " + path;
system( cmd.c_str() );
if( IsDirectory( path ) == false )
{
cerr << "can't make log directory. path=" << path << "." << endl;
return false;
}
return true;
}
bool CLogger::IsDirectory( string path )
{
struct stat dirStat;
if( lstat ( path.c_str(), &dirStat ) != 0 )
{
cerr << "Log path not valid. Check path [" << path << "][" << errno << "][" << strerror(errno) << "]" << endl;
return false;
}
// 해당 정보가 Directory 가 아닌 경우
if( S_ISDIR( dirStat.st_mode ) == false )
{
return false;
}
return true;
}
void CLogger::Exit()
{
if( m_bIsInitialized == false )
{
return;
}
pthread_mutex_lock( &m_mutex );
if( CLogger::m_pInstance != NULL )
{
delete CLogger::m_pInstance;
CLogger::m_pInstance = NULL;
m_bIsInitialized = false;
}
pthread_mutex_unlock( &m_mutex );
}
CLogger* CLogger::GetInstance()
{
return CLogger::m_pInstance;
}
string CLogger::GetLogFilename( struct tm &timeNow )
{
char timeStr[256];
snprintf( timeStr, (size_t)256, "%04d%02d%02d.log", timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday);
// Make File Name
string filename = m_szLogDir + "/" + m_szProgramName + "/" + m_szProgramName + "_" + string( timeStr );
return filename;
}
bool CLogger::Write( int logLevel, const char * fmt, ...)
{
// 가변 인자 처리
va_list args;
char buffer[MAX_BUFFER_SIZE];
va_start( args, fmt );
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
{
va_end( args );
return false;
}
va_end(args);
return Write( logLevel, PREFIX_DATE, NULL, 0, buffer );
}
bool CLogger::WriteNoPrefix( int logLevel, const char * fmt, ...)
{
// 가변 인자 처리
va_list args;
char buffer[MAX_BUFFER_SIZE];
va_start( args, fmt );
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
{
va_end( args );
return false;
}
va_end(args);
return Write( logLevel, PREFIX_NONE, NULL, 0, buffer );
return false;
}
// CHG 2012-08-16 huibong
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
// 함수명 : Write -> WriteWithFunc 으로 변경 처리
bool CLogger::WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
{
if( filename == NULL || funcname == NULL || lineNum < 0 || fmt == NULL )
{
return false;
}
// 가변 인자 처리
va_list args;
char buffer[MAX_BUFFER_SIZE];
va_start( args, fmt );
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
{
va_end( args );
return false;
}
va_end(args);
char className[255];
string funcPrefix = GetClassName( className, filename );
funcPrefix += "::" + string( funcname ) + "()";
return Write( logLevel, PREFIX_FUNCTION, funcPrefix.c_str(), lineNum, buffer );
}
const char* CLogger::GetClassName( char* className, const char* filename )
{
if( className == NULL )
{
cout << "[ERROR] The input argument 'className' is NULL." << endl;
return NULL;
}
if( filename == NULL )
{
cout << "[ERROR] The input argument 'filename' is NULL." << endl;
strcpy( className, "NULL" );
return className;
}
int filenameSize = strlen( filename );
memcpy( className, filename, filenameSize );
// find end position
char endCharacter = '.';
int endPos = filenameSize - 1;
for( ; endPos > 0; --endPos )
{
if( className[endPos] == endCharacter )
{
break;
}
}
if( endPos == 0 )
{
cout << "[ERROR] endPos=0" << endl;
strcpy( className, "NULL" );
return className;
}
className[endPos] = '\0';
// find start position
char startCharacter = '/';
int startPos = endPos - 1;
for( ; startPos > 0; --startPos )
{
if( className[startPos] == startCharacter )
{
break;
}
}
if( startPos != 0 )
{
startPos += 1;
}
return (className + startPos );
}
bool CLogger::Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log )
{
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
if( IsValidLogLevel( logLevel ) == false )
{
return false;
}
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
if( logLevel > m_nLogLevel )
{
return false;
}
if( log == NULL )
{
return false;
}
// Get Current Data & Time
time_t now = time( NULL );
struct tm timeNow;
localtime_r( &now, &timeNow );
string filename = GetLogFilename( timeNow );
// Log file open
FILE* pFile = NULL;
pFile = fopen( filename.c_str(), "a+" );
if( pFile == NULL )
{
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
return false;
}
// 시간 정보처리
char timeStr[256];
snprintf( timeStr, (size_t)256, "[%02d:%02d:%02d]"
, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec );
// Log Level 문자열 검색
string szLevel = m_vectorLogLevelString[ logLevel ];
switch( logPrefix )
{
case PREFIX_DATE:
/// 형식 예: [15:47:41] [DBG] sample log message.
fprintf( pFile, "%s [%-4s] %s\n", timeStr, szLevel.c_str(), log );
break;
case PREFIX_FUNCTION:
if( functionName == NULL )
{
fclose( pFile );
return false;
}
/// 형식 예: [15:47:41] [DBG] sample log message. [SomeClass::SomeFunction():12]
fprintf( pFile, "%s [%-4s] %s [%s:%d]\n", timeStr, szLevel.c_str(), log, functionName, lineNum );
break;
case PREFIX_NONE:
/// 형식 예: sample log message.
fprintf( pFile, "%s\n", log );
break;
}
// Write to log file
fflush( pFile );
// 종료 처리.
fclose( pFile );
return true;
}
bool CLogger::WriteHex( int logLevel, const unsigned char* data, const int size )
{
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
if( IsValidLogLevel( logLevel ) == false )
{
return false;
}
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
if( logLevel > m_nLogLevel )
{
return false;
}
if( data == NULL )
{
return false;
}
if( size <= 0 )
{
return false;
}
// Get Current Data & Time
time_t now = time( NULL );
struct tm timeNow;
localtime_r( &now, &timeNow );
string filename = GetLogFilename( timeNow );
// Log file open
FILE* pFile = NULL;
pFile = fopen( filename.c_str(), "a+" );
if( pFile == NULL )
{
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
return false;
}
for( int i = 0; i < size; ++i )
{
fprintf( pFile, "%02X", data[i] );
if( i == 0 )
{
continue;
}
if( ( (i+1) % 8 ) == 0 )
{
fprintf( pFile, " " );
}
if( ( (i+1) % 16 ) == 0 )
{
fprintf( pFile, " " );
}
if( ( (i+1) % 32 ) == 0 )
{
fprintf( pFile, "\n" );
}
}
fprintf( pFile, "\n" );
fflush( pFile );
// 종료 처리.
fclose( pFile );
return false;
}
+273
View File
@@ -0,0 +1,273 @@
/***************************************************************************
Logger.h
-----------------------------------------
begin : 2011/10/26
copyright : (C) 2005 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.2.0.805
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __LOGGER_H__
#define __LOGGER_H__
#include <string>
#include <vector>
#include <pthread.h>
///@brief 로그 레벨 정의
#define LEMR 0 /* system is or will be unusable if situation is not resolved */
#define LALT 1 /* immediate action required */
#define LCRT 2 /* critical situations */
#define LERR 3 /* error conditions */
#define LWAR 4 /* recoverable errors */
#define LNOT 5 /* unusual situation that merits investigation */
#define LINF 6 /* information messages */
#define LDBG 7 /* verbose data for debugging */
#define LDEV 8 /* verbose data for developer */
#define LDEV1 9 /* start or end the function of application level */
#define LDEV2 10 /* start or end the function of application level */
#define MAX_LOG_LEVEL LDEV2
///@brief Log 정보를 파일로 저장하기 위한 Class.
///1. 가변 format 으로 전달된 로그 관련 정보를 Log Level 에 따라 로그 파일에 아래의 4가지 형식으로 저장한다.
///
/// 1.1 Prefix로 [hh:mm:ss]와 [ClassName::FunctionName]이 추가된 로그
/// LOG( level, format, ... ) 매크로 사용.
/// 예) [17:43:40] [DBG] log level debug [LoggerTestTestLogger]
///
/// 1.2 Prefix로 [hh:mm:ss]이 추가된 로그
/// _LOG( level, format, ... ) 매크로 사용
/// 예) [17:43:40] [EMR] log level emergency
///
/// 1.3 Prefix가 없는 로그
/// _LOG_( level, format, ... ) 매크로 사용
/// 예) log level emergency
///
/// 1.4 Hex 로그.
/// LOG_HEX( level, data, size ) 매크로 사용
/// 예) 00010203 04050607 08090A0B 0C0D0E0F
///
///2. Logger 객체 초기화시 전달된 Log Level 정보보다 전달받은 Log Level 정보가 큰 경우 해당 로그는 파일로 저장되지 않는다.
///
///3. 로그 파일은 매 일단위로 저장파일이 변경된다.
///
///4. 로그 저장을 위한 program 경로가 존재하지 않는 경우 자동 생성 처리된다.
///
///5. 로그 저장방식은 매 저장로그마다 open-close 로 처리된다.
///
///6. 파일로 기록시 Log Level 에 대한 정보도 함께 기록된다.
///
///7. 싱글톤으로 작성되었고, CLogger::Init(...)시에 쓰레드 안정성을 제공한다.
///
///8. 동적으로 로그 레벨을 변경할 수 있는 인터페이스를 제공한다.
///
class CLogger
{
// Attributes
private:
///@brief 싱글톤 객체 인트턴스.
static CLogger* m_pInstance;
///@brief 싱글톤 객체 초기화 여부.
static bool m_bIsInitialized;
///@brief 싱글톤 객체 초기화시 스레드 안정성을 위한 뮤텍스.
static pthread_mutex_t m_mutex;
///@brief 프로그램 이름. 로그 파일 경로를 만들 때 사용.
std::string m_szProgramName;
///@brief 공통 로그 디렉토리 이름. 로그 파일 경로를 만들 때 사용.
std::string m_szLogDir;
///@brief 로그 레벨.
int m_nLogLevel;
///@brief 로그 레벨에 대응되는 문자열 정보를 저장.
std::vector<std::string> m_vectorLogLevelString;
protected:
public:
///@brief 로그 형식을 지정.
typedef enum
{
PREFIX_NONE = 0, /// 클래스 설명의 1.3에 해당
PREFIX_DATE, /// 클래스 설명의 1.2에 해당
PREFIX_FUNCTION, /// 클래스 설명의 1.1에 해당
} LOG_PREFIX;
// Operations
private:
///@brief 생성자.
/// 프로그램 이름, 공통 로그 디렉토리, 로그 레벨을 저장하고
/// 로그 레벨에 대응되는 문자열 정보를 만든다.
///@param programName [in] 프로그램 이름
///@param logDir [in] 공통 로그 디렉토리 경로
///@param logLevel [in] 로그 레벨
CLogger( std::string programName, std::string logDir, int logLevel );
///@brief 소멸자.
/// 로그 레벨에 대응되는 문자열 정보를 저장하고 있는
/// 벡터 m_vectorLogLevelString을 clear 시킴.
virtual ~CLogger();
///@brief 로그 레벨에 대응되는 문자열을 만든다.
///@param none.
///@return none.
void MakeLogLevelString();
///@brief 로그 파일이 위치할 실제 로그 디렉토리를 생성한다.
/// 생성할 디렉토리 경로는 'logDir/programName'이 된다.
///@param programName [in] 프로그램 이름
///@param logDir [in] 공통 로그 디렉토리 경로
///@return 디렉토리가 이미 존재하거나 생성 성공하면 true,
/// 해당 경로가 존재하지만 디렉토리가 아니거나, 디렉토리 생성 실패하면 false 반환.
static bool MakeLogDir( std::string programName, std::string logDir );
///@brief 해당 경로가 디렉토리 인지 아닌지 판단.
///@param path [in] 디렉토리 인지 아닌지 판단할 경로.
///@return 해당 경로가 디렉토이면 true,
/// 경로가 존재하지 않거나 디렉토리가 아니면 false 반환.
static bool IsDirectory( std::string path );
///@brief 로그 레벨이 올바른지 판별.
///@param logLevel [in]
///@return 올바른 로그 레벨이면 true, 그렇지 않으면 false 반환.
static bool IsValidLogLevel( int logLevel );
///@brief 시간 정보를 입력 받아 로그 파일 이름을 만든다.
/// 로그 파일 이름 형식 : 프로그램명_YYYYMMDD.log
///param timeNow [in] 현재 시간 정보.
///return 로그 파일 이름.
std::string GetLogFilename( struct tm &timeNow );
///@brief 로그를 남기는는 클래스가 정의된 파일의 이름에서 클래스명을 추출한다.
/// 쓰레드 안정성을 보장한다.
///@param className [out] 파일 이름에서 추출된 클래스명
///@param filename [in] 파일 이름.
///@return 클래스명 추출이 성공하면 클래스명 문자열의 포인터, 추출 실패하면 NULL.
const char* GetClassName( char* className, const char* filename );
///@brief 인자 logPrefix에 따라 적절한 형식으로 로그 파일에 로그를 저장한다.
/// 인자 logLevel이 설정된 로그 레벨보다 높으면 로그를 출력하지 않는다.
///@param logLevel [in] 로그 레벨
///@param logPrefix [in] 로그 프리픽스 종류.
///@functionName [in] ClassName::FunctionNmae() 형식의 문자열.
///@lineNum [in] 라인 번호.
///@log [in] 출력하고자 하는 로그 내용.
bool Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log );
protected:
public:
///@brief 싱글톤 객체 m_pInstance를 생성하고 인자 정보로 로그 디렉토리를 만든다.
///@param programName [in] 프로그램 이름
///@param logDir [in] 공통 로그 디렉토리 경로
///@param logLevel [in] 로그 레벨
///@return 로그 디렉토리를 만들고 싱글톤 객체를 생성했으면 true,
/// 로그 디렉토리를 만들지 못 했거나 인자 값이 올바르지 않으면 false 반환.
static bool Init( std::string programName, std::string logDir, int logLevel );
///@brief 싱글톤 객체 m_pInstance를 delete 한다.
///@param none.
///@return none.
static void Exit();
///@brief 싱글톤 객체 m_pInstance를 반환한다.
///@param none.
///@return CLogger 객체의 인스턴스.
static CLogger* GetInstance();
///@brief Prefix로 [hh:mm:ss]이 추가된 형식으로 로그 저장.
///@param logLevel [in] 로그 레벨
///@param fmt [in] 로그 내용 포맷.
///@param __VAR_ARGS__ [in] 가변 인자.
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
bool Write( int logLevel, const char * fmt, ...)
__attribute__((format(printf, 3, 4)));
///@brief Prefix가 없는 로그 저장.
///@param logLevel [in] 로그 레벨
///@param fmt [in] 로그 내용 포맷.
///@param __VAR_ARGS__ [in] 가변 인자.
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
bool WriteNoPrefix( int logLevel, const char * fmt, ...)
__attribute__((format(printf, 3, 4)));
// CHG 2012-08-16 huibong
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
///@brief Prefix로 [hh:mm:ss]와 [ClassName::FunctionName:line]이 추가된 형식으로 로그 저장.
///@param logLevel [in] 로그 레벨
///@param filename [in] 파일 이름
///@param funcname [in] 함수 이름
///@param lineNum [in] 라인 번호
///@param fmt [in] 로그 내용 포맷.
///@param __VAR_ARGS__ [in] 가변 인자.
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
bool WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
__attribute__((format(printf, 6, 7)));
///@brief 로그를 hex 형식으로 저장.
///@param logLevel [in] 로그 레벨
///@param data [in] hex 형식으로 출력할 데이터.
///@pram size [in] data의 크기.
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
bool WriteHex( int logLevel, const unsigned char* data, const int size );
///@brief 로그을 설정한다. 인자 logLevel이 적절한 값이면 새로운 값으로 변경하고
/// 적절한 값이 아니면 로그 레벨을 변경하지 않는다.
///@param logLevel [in] 설정할 로그 레벨
///@return none.
bool SetLogLevel( int logLevel );
inline int GetLogLevel() { return m_nLogLevel; };
inline std::string GetLogDir() { return m_szLogDir + "/" + m_szProgramName; };
};
#define LOG( level, format, ... ) \
if( CLogger::GetInstance() != NULL ) \
{ \
CLogger::GetInstance()->WriteWithFunc( level, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__ ); \
}
#define _LOG( level, format, ... ) \
if( CLogger::GetInstance() != NULL ) \
{ \
CLogger::GetInstance()->Write( level, format, ##__VA_ARGS__ ); \
}
#define _LOG_( level, format, ... ) \
if( CLogger::GetInstance() != NULL ) \
{ \
CLogger::GetInstance()->WriteNoPrefix( level, format, ##__VA_ARGS__ ); \
}
#define _LOG_HEX_( level, data, size ) \
if( CLogger::GetInstance() != NULL ) \
{ \
CLogger::GetInstance()->WriteHex( level, (const unsigned char*)data, size ); \
}
#define FUNC_BEGIN() LOG( LDEV1, "begin" )
#define FUNC_END() LOG( LDEV1, "end" )
#define FRM_BEGIN() LOG( LDEV2, "begin" )
#define FRM_END() LOG( LDEV2, "end" )
#endif // __LOGGER_H__
+69
View File
@@ -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
+185
View File
@@ -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;
}
+37
View File
@@ -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__
+152
View File
@@ -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;
}
+62
View File
@@ -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__ */
+35
View File
@@ -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
#-----------------------------------------------#
+169
View File
@@ -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;
}
+75
View File
@@ -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__ */
+184
View File
@@ -0,0 +1,184 @@
/***************************************************************************
Database Class
-----------------------------------------
begin : 2010/03/09
copyright : (C) 2010 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include "Database.h"
#include "Logger.h"
DataBase::~DataBase()
{
if(m_PGconn != NULL) {
PgCloseDB();
}
}
PGconn *DataBase::PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout)
{
ostringstream conninfo;
// CHG 2014-10-29 huibong
// application_name add to logging RCDB
conninfo << "host=" << strHost << " port=" << Port << " dbname=" << strDBName <<
" user=" << strAcct << " password=" << strPasswd << " connect_timeout=" << timeout <<
" application_name=" << PROG_NAME;
#ifdef _USE_LIBPQ_KEEPALIVE
// This option is supported by at least 9.1.2. and Currently supports Linux systems.
conninfo <<" keepalives=1";
#endif // _USE_LIBPQ_KEEPALIVE
//m_PGconn = PQsetdbLogin(strHost.c_str(), szPort, NULL, NULL, strDBName.c_str(), strAcct.c_str(), strPasswd.c_str());
m_PGconn = PQconnectdb(conninfo.str().c_str());
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
return NULL;
}
return m_PGconn;
}
PGconn *DataBase::PgOpenDB(const char *pszDBName)
{
m_PGconn = PQsetdb(NULL, NULL, NULL, NULL, pszDBName);
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
return NULL;
}
return m_PGconn;
}
void DataBase::PgCloseDB()
{
if(m_PGconn != NULL)
{
PQfinish(m_PGconn);
m_PGconn = NULL;
}
}
int DataBase::PgResult(CFLAG flag)
{
int fRet = 0;
int Result;
Result = PQresultStatus(m_pRes);
switch(Result) {
case PGRES_EMPTY_QUERY :
fRet = -1;
break;
case PGRES_BAD_RESPONSE :
fRet = -2;
break;
case PGRES_NONFATAL_ERROR :
fRet = -3;
break;
case PGRES_FATAL_ERROR :
fRet = -4;
break;
case PGRES_TUPLES_OK :
fRet = 1;
break;
case PGRES_COMMAND_OK :
fRet = 2;
break;
}
if(fRet < 0) {
m_ErrorMessage = PQresultErrorMessage(m_pRes);
}
m_ResultCode = Result;
if(flag == CLEAR && m_pRes) {
PQclear(m_pRes);
m_pRes = NULL;
}
return fRet;
}
string &DataBase::GetErrorMessage()
{
return m_ErrorMessage;
}
int DataBase::GetCmdTuples()
{
//fprintf(stderr, "DataBase::GetCmdTuples PQcmdTuples %s\n", PQcmdTuples(m_pRes));
return (int)atoi(PQcmdTuples(m_pRes));
}
int DataBase::GetNoTuples()
{
return PQntuples(m_pRes);
}
int DataBase::GetNoFields()
{
return PQnfields(m_pRes);
}
PGresult *DataBase::GetRes()
{
return m_pRes;
}
void DataBase::PgClear()
{
if(m_pRes)
{
PQclear(m_pRes);
m_pRes = NULL;
}
}
char *DataBase::GetValue(int tuple, int field)
{
return PQgetvalue(m_pRes, tuple, field);
}
int DataBase::PgDoExec(char *pszQuery)
{
return this->PgDoExec(pszQuery, NOT_CLEAR);
}
int DataBase::PgDoExec(string &strQuery)
{
return this->PgDoExec((char *)strQuery.c_str(), NOT_CLEAR);
}
int DataBase::PgDoExec(char *pszQuery, CFLAG flag)
{
if(m_PGconn == NULL) return -1;
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
m_pRes = PQexec(m_PGconn, pszQuery);
int r = PgResult(flag);
return r;
}
int DataBase::PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag)
{
if(m_PGconn == NULL) return -1;
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
m_pRes = PQexecParams(m_PGconn, pszQuery, nParamCnt, NULL, paramValues, NULL, NULL, 0);
int r = PgResult(flag);
return r;
}
int DataBase::PgEscapeString(char *to, const char *from, size_t length)
{
int retval = 0;
//PQescapeStringConn(m_PGconn, to, from, length, &retval);
PQescapeString(to, from, length);
return retval;
}
+56
View File
@@ -0,0 +1,56 @@
/***************************************************************************
Database Class
-----------------------------------------
begin : 2010/03/11
copyright : (C) 2010 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __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__
+579
View File
@@ -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;
}
+70
View File
@@ -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__ */
+80
View File
@@ -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
+250
View File
@@ -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;
}
+117
View File
@@ -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__ */
+40
View File
@@ -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;
}
+52
View File
@@ -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__ */
+43
View File
@@ -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;
}
+61
View File
@@ -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__ */
+274
View File
@@ -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;
}
+96
View File
@@ -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__ */
+681
View File
@@ -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;
}
+38
View File
@@ -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__ */
+5
View File
@@ -0,0 +1,5 @@
// sample 소스 파일
#include ""
+32
View File
@@ -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__ */
+20
View File
@@ -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