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
+111
View File
@@ -0,0 +1,111 @@
Revision 1440
-------------------
수정일 : 2018-02-26
수정자 : 유희곤
- CHG: 공유메모리 접근 key path 변경 (#31703)
- /tmp 폴더에 대한 자동 삭제 처리 문제로
- 공유 메모리 접근 key path 를 /user/service 로 변경 처리
- CHG: FHS data 수신 관련 timeout 발생시 session 종료 처리 (#31703)
- FHS Hang 상황시 TCP 세션이 그대로 유지되는 문제 발생
- 기존에는 세션을 계속 유지했으나...
- 여러 운영관련 오해 상황 발생 및 빠른 감지 처리를 위해
- FHS 의 service, system 정보 수신 관련 timeout 발생시 세션 종료하도록 수정
- NEW: 서비스 상태 변경시 report 로깅 처리 (#31703)
- 서비스 상태가 ok, read only -> diable, out 상태로 변경시 report 로깅
- 서비스 상태가 disable, out -> ok, read only 로 변경시 report 로깅
- CHG: 메모리 사용률 감시 관련 conf 상의 default 값 99 -> 100 % 변경 (#31703)
- 실제 메모리 사용률이 99% 수준인 RC 존재하여
- conf 상의 값을 99 -> 100 으로 변경 처리
- CHG: r1435 에서 추가한 disk busy 상황시 자동 FHS out 처리 관련 기능 제거 (#31703)
- 실제 서비스 적용 후 오탐 상황 발생
- 즉 종종 disk queue 10, busy 90% 이상인 상황 발생했지만. FHS 는 정상 상태
- 따라서 해당 기능 제거 처리, conf 상에서 해당 설정 제거
- 다만 report 로깅 기능은 그대로 유지
- CHG: rc_show 개선 (#31703)
- 실제 유효한 세션만 표시되도록 수정 처리
- data 출력 포맷 조정 및 순서 조정
- 예전 vmasd 관련 항목 제거
- CHG: 수정된 rcts.conf 등록
- rcts.conf.in.R1440
Revision 1435
-------------------
수정일 : 2018-01-12
수정자 : 유희곤
- NEW: FHS 메모리 사용량 report 로깅 기능 추가 (#31360, #31679)
- FHS 의 memory usage % 정보가 conf 의 설정값 이상인 경우 report log 에 기록하도록 신규 기능 추가
- 잦은 로깅 방지를 위해 분당 1회만 Check 수행.
- conf 상에 설정값(REPORT_MEMORY_MAX_USAGE) 이 없는 경우 해당 기능 비활성화됨.
- NEW: FHS Disk hang 또는 이상 상황으로 disk busy 비정상 증가시 자동 FHS out 처리 (#31544, #31679 )
- disk busy 90.0 %, disk queue 값이 연속으로 10 이상인 경우 FHS 자동 OUT 처리
- conf 상 설정 값이 따라 FHS 자동 OUT 처리 여부 정의 (FHS_OUT_WHEN_DISK_BUSY)
- 자동 FHS OUT 처리된 경우... csagentd 재기동 또는 수동 fhs.out 설정시 까지 상태 유지됨
- CHG: 3.4 버전 지원을 위한 vmasd 관련 기능 비활성화
- 소스 확인 결과.. rcts.conf 상에 FHS_STATUS_BROADCAST_UDP_PORT 설정이 없는 경우
- 관련 모듈들 비활성화 되므로... 해당 설정만 비활성화해서 패키징 하기로 결정
- CHG: 수정된 rcts.conf 등록
- rcts.conf.in.R1435
Revision 1349
-------------------
수정일 : 2016-04-07
수정자 : 유희곤
- CHG: rc_show TCP 세션 출력 관련 수정
- rc_show 의 TCP 세션 출력이 3 자리 설정된 상태이나..실서비스에서는 5자리 까지 출력 상황 발생
- 이에 TCP 세션 출력이 5자리 처리되도록 수정
* 본 수정 내역은 중요 사항이 아니므로.. binary 생성 및 tagging 처리는 수행하지 않습니다.
Revision 1279
-------------------
수정일 : 2015-11-03
수정자 : 유희곤
- CHG: rc_show 기능 추가 (#24963)
- help 기능 추가
- 버전 표시 기능 추가
- 인자값 옵션 처리 기능 추가
- 버그 발생 관련 코드 수정
- CHG: Database.cpp 관련 다른 프로세스에서 발견된 중복 코드 수정.
- PgDoExecParams() 함수의 m_pRes 중복 대입 코드 수정.
Revision 1222
-------------------
수정일 : 2015-09-10
수정자 : 유희곤
- CHG: Makefile install 스크립트 오류 수정
- rc_show 모듈 설치 기능 추가
Revision 1221
-------------------
수정일 : 2015-09-10
수정자 : 유희곤
- NEW: SVN 신규 등록
- 신규 rc_mond 에 대한 SVN 등록
- 본 모듈 관련 자세한 사항은 redmine #22682 이슈 참고
- 설계서: svn://svc1svn.solbox.com/document/01 양방향/02 설계서/25 FHS 상태 정보 수집&전송/rc_mond 설계서.vsdx
- 통신 프로토콜: svn://svc1svn.solbox.com/document/01 양방향/02 설계서/25 FHS 상태 정보 수집&전송/[rc_mond] 통신프로토콜구조.xlsx
- NEW: 버전 정책 변경 내역 반영 (#22504)
- 3.5.0 으로 적용.
+37
View File
@@ -0,0 +1,37 @@
#****************************************************************************
# Makefile for rc_statd
# -----------------------------------------
#
# begin : 2015/05/28
# 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
+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__ */
+110
View File
@@ -0,0 +1,110 @@
/***************************************************************************
Fixed size Queue Class ( FixedQueue.cpp )
-----------------------------------------
begin : 2013/08/09
copyright : (C) 2005 SolutionBox Inc.
author : Development 1 Team
email : dev1@solbox.com
version : 3.2
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 "FixedQueue.h"
#include <string.h>
CMutexLock::CMutexLock(pthread_mutex_t * mutex)
: m_plock(mutex)
{
pthread_mutex_lock(m_plock);
}
CMutexLock::~CMutexLock()
{
pthread_mutex_unlock(m_plock);
}
CFixedQueue::CFixedQueue(int bufferSize, int qSzie )
: m_bufferSize(bufferSize), m_queueSize(qSzie)
{
pthread_mutex_init(&m_mutex, NULL);
}
CFixedQueue::~CFixedQueue()
{
ReleaseAll();
pthread_mutex_destroy(&m_mutex);
}
void CFixedQueue::ReleaseAll()
{
pthread_mutex_unlock(&m_mutex);
while (!m_queue.empty())
{
FixedQueueData *d = m_queue.front();
if(d) delete (FixedQueueData*) d;
m_queue.pop();
}
}
int CFixedQueue::Push(char* buf, size_t buffersize)
{
int r = 0;
if(buf == NULL)
return -1;
if(buffersize > (size_t) m_bufferSize)
return -2;
CMutexLock lock (&m_mutex);
do
{
if( m_queue.size() >= std::queue<char*>::size_type(m_queueSize) )
{
r = 1;
break;
}
FixedQueueData *d = new FixedQueueData;
d->data_size = buffersize;
d->data = new char [buffersize];
memcpy(d->data, buf, buffersize);
m_queue.push(d);
r = 0;
}while (false);
return r;
}
int CFixedQueue::Pop(char* buf, size_t buffersize)
{
int r = 0;
if (buf == NULL)
return -1;
memset(buf, 0, buffersize);
CMutexLock lock (&m_mutex);
do
{
if( m_queue.empty())
{
r = 1;
break;
}
FixedQueueData *t = m_queue.front();
memcpy(buf, t->data, t->data_size);
delete (FixedQueueData *) t;
m_queue.pop();
r = 0;
} while (false);
return r;
}
+81
View File
@@ -0,0 +1,81 @@
/***************************************************************************
Fixed size Queue Class Header ( FixedQueue.h )
-----------------------------------------
begin : 2013/08/09
copyright : (C) 2005 SolutionBox Inc.
author : Development 1 Team
email : dev1@solbox.com
version : 3.2
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 __FIXED_SIZE_QUEUE_H__
#define __FIXED_SIZE_QUEUE_H__
#include <pthread.h>
#include <queue>
class CMutexLock
{
public:
CMutexLock(pthread_mutex_t * mutex);
~CMutexLock();
private:
pthread_mutex_t* m_plock;
};
class FixedQueueData
{
public:
size_t data_size;
char * data;
FixedQueueData ()
: data_size(0), data(NULL) {};
~FixedQueueData () { if(data) delete [] data; }
};
class CFixedQueue
{
public:
///@brief 생성자.
///@param bufferSize [in] 데이터 버퍼 크기
///@param qSzie [in] 큐의 크기
CFixedQueue (int bufferSize, int qSzie );
///@brief 소멸자.
~CFixedQueue();
///@brief 데이터 삽입
///@param buf [in] 데이터
///@param buffersize [in] 데이터 크기
///@return 0: 정상, 1:queue full, -1 : error
int Push(char* buf, size_t buffersize);
///@brief 데이터 추출
///@return 데이터 리턴(FIFO)
///@param buf [in] 데이터
///@param buffersize [in] 데이터 크기
///@return 0: 정상, 1: queue empty 나머지 : error
int Pop(char* buf, size_t buffersize);
protected:
///@brief 큐에 초기화
void ReleaseAll();
private:
int m_bufferSize;
int m_queueSize;
pthread_mutex_t m_mutex;
std::queue<FixedQueueData *> m_queue;
};
#endif // __FIXED_SIZE_QUEUE_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
# - 2013/06/04 - 1st dadamin
# 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 FixedQueue.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__ */
+46
View File
@@ -0,0 +1,46 @@
[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
LOG_LEVEL = 7
# RC ID
RC_ID = bd16
# RCDB Access Information
RCDB_IP = 10.3.0.3
RCDB_PORT = 6543
RCDB_DB_NAME = localdb
RCDB_ACCT = syshost
RCDB_ACCT_PW = alfkzmf!?{}
# FHS status broadcasting UDP port ( vmasd broadcasting udp port )
# 0 : Not use.
#FHS_STATUS_BROADCAST_UDP_PORT = 13201
#-----------------------------------------------#
[rc_mond]
# FHS csagentd send data(system, service check info) receiving TCP port
PORT_TCP_LISTEN_SYSTEM_STAT = 14003
PORT_TCP_LISTEN_CHECK_STAT = 14004
# FHS fimngd communication TCP port
# - alive fhs list....
PORT_TCP_LISTEN_SEND_INFO = 14005
# GMS (new_sh_statusd)
GMS_SERVER = cc-gms.ktsh.co.kr
GMS_PORT_STAT = 13109
# Max storage usage(%) for prevent content upload
# range : 0 ~ 100
STORAGE_MAX_USAGE_TO_DISABLE = 98
# Check FHS memory max usage percent and report (range: 0 ~ 100 )
# use default 0 (not check) when not config
REPORT_MEMORY_MAX_USAGE = 100
#-----------------------------------------------#
+143
View File
@@ -0,0 +1,143 @@
#include "AliveFhsCacheThread.h"
#include "Logger.h"
#include <errno.h>
#include <string.h>
// alive fhs 정보의 cache 갱신 주기 ( sec )
#define ALIVE_FHS_CACHE_INTERVAL 1
// 공유메모리 FHS 정보가 기동초기 완전하지 않아...
// 지정된 시간(sec) 동안 skip 처리 하기 위한 define.
#define ALIVE_FHS_SKIP_TIME 6
// 생성자
CAliveFhsCacheThread::CAliveFhsCacheThread( CShmManager * pShmManager )
: m_pShmManager( pShmManager )
{
// Thread handle 초기화
m_threadHandle = 0;
// vector 객체에 대한 포인터 초기화.
m_vecAliveFhs1.clear();
m_vecAliveFhs2.clear();
m_pCurrent = &( m_vecAliveFhs1 );
m_pOld = &( m_vecAliveFhs2 );
// Skip flag 는 기동 초기 설정 처리.
m_bResultSkip = true;
}
// 소멸자
CAliveFhsCacheThread::~CAliveFhsCacheThread()
{
// Thread 가 동작 중인 경우 Thread 동작 정지 처리.
if( m_threadHandle != 0 )
{
pthread_cancel( m_threadHandle );
}
}
// Thread 를 생성하여 cache 작업 수행
bool CAliveFhsCacheThread::Start( void )
{
int nResult = pthread_create( &m_threadHandle, NULL, CAliveFhsCacheThread::threadFunc, this );
if( nResult != 0 )
{
// Thread 생성 실패시...
int errorNum = errno;
LOG( LERR, "AliveFhsCacheThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// Thread 함수
void * CAliveFhsCacheThread::threadFunc( void * arg )
{
CAliveFhsCacheThread * pObject = reinterpret_cast<CAliveFhsCacheThread *>( arg );
pthread_detach( pthread_self() );
// SKIP 상태 해지를 판단하기 위한 변수.
int nSkipCount = 0;
// 주기적으로 alive FHS cache 갱신 처리.
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 지정된 횟수를 초과하면 Skip 처리를 해제한다.
if( pObject->m_bResultSkip == true )
{
if( nSkipCount > ALIVE_FHS_SKIP_TIME )
{
pObject->m_bResultSkip = false;
_LOG( LINF, "AliveFhsCacheThread: alive FHS SKIP mode end. ");
}
else
{
++nSkipCount;
//_LOG( LDBG, "AliveFhsCacheThread: Alive FHS SKIP..." );
}
}
// 지정된 주기마다 cache 갱신.
sleep( ALIVE_FHS_CACHE_INTERVAL );
}
// Thread 종료시 Handle 초기화
pObject->m_threadHandle = 0;
return NULL;
}
// 실제 cache 처리를 담당하는 함수.
void CAliveFhsCacheThread::Execute()
{
// 이전 정보를 저장한 vector 초기화.
m_pOld->clear();
// 공유메모리로부터 Alive FHS 정보 저장 처리
m_pShmManager->GetAliveFhsList( *m_pOld );
// 최신 Alive FHS 정보를 사용하도록 포인터 정보 변경 처리.
m_pTemp = m_pCurrent;
m_pCurrent = m_pOld;
m_pOld = m_pTemp;
// SKIP 모드가 아닌 상태에서 Alive FHS 정보가 유효하지 않은 경우만 로깅 처리.
if( m_pCurrent->size() == 0 && m_bResultSkip == false )
{
_LOG( LWAR, "AliveFhsCacheThread: Alive FHS not exist. [%zu]", m_pCurrent->size());
}
return;
}
// Alive FHS 목록 제공을 위한 interface 함수.
// param vecResult [out] alive fhs 결과 정보를 저장하기 위한 변수.
// return -1 : FHS 정보가 아직 불안전하니 SKIP 처리하라는 의미
// 그 외 : alive fhs 갯수.
int CAliveFhsCacheThread::GetAliveFhs( std::vector<struct FhsServiceStatInfo> & vecResult )
{
// skip flag 가 설정된 경우...
if( m_bResultSkip == true )
return -1;
else
{
// 정상 상황인 경우...
vecResult = *( m_pCurrent );
return m_pCurrent->size();
}
}
+86
View File
@@ -0,0 +1,86 @@
/****************************************************************************
Alive FHS list cache thread class
-----------------------------------------
begin : 2015/07/17
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 __ALIVE_FHS_CACHE_THREAD_H__
#define __ALIVE_FHS_CACHE_THREAD_H__
#include "ShmDefine.h"
#include "ShmManager.h"
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <vector>
// 공유메모리 상에서 Alive FHS 정보를 주기적으로 조회하여 저장하고...
// interface 함수를 통해 해당 정보를 전달 처리하기 위한 Thread Class.
class CAliveFhsCacheThread
{
public:
// 생성 및 소멸자.
CAliveFhsCacheThread( CShmManager * pShmManager );
~CAliveFhsCacheThread();
// Thread 를 생성하여 cache 작업 수행
bool Start( void );
// Alive FHS 목록 제공을 위한 interface 함수.
// param vecResult [out] alive fhs 결과 정보를 저장하기 위한 변수.
// return -1 : FHS 정보가 아직 불안전하니 SKIP 처리하라는 의미
// 그 외 : alive fhs 갯수.
int GetAliveFhs( std::vector<struct FhsServiceStatInfo> & vecResult );
private:
// 공유메모리에서 Alive FHS 목록 추출시 사용할 공유메모리 inferface 객체 (생성자를 통해 전달받음)
CShmManager * m_pShmManager;
// 쓰레드 핸들
pthread_t m_threadHandle;
// Thread 기동 후 지정된 시간 동안 공유메모리 FHS 정보가 완전하지 않아.. SKIP 하라는 flag
bool m_bResultSkip;
// Alive FHS 목록을 저장할 Vector 객체
// - struct FhsServiceStatInfo 는 ShmManager.h 에 정의되어 있음.
// - 2개의 list 를 이용하여 목록을 교차하여 갱신처리.
std::vector<struct FhsServiceStatInfo> m_vecAliveFhs1;
std::vector<struct FhsServiceStatInfo> m_vecAliveFhs2;
// lock 처리를 최소화하기 위해 포인터를 이용하여 구현.
std::vector<struct FhsServiceStatInfo> * m_pCurrent;
std::vector<struct FhsServiceStatInfo> * m_pOld;
std::vector<struct FhsServiceStatInfo> * m_pTemp;
private:
// Thread function....
static void * threadFunc( void * arg );
// 실제 cache 처리를 담당하는 함수.
void Execute( void );
};
#endif /* __ALIVE_FHS_CACHE_THREAD_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 __DATABASE_H__
#define __DATABASE_H__
#include <iostream>
#include <string>
#include "libpq-fe.h"
#define MaxSizeOfDBQuery 1024*9
using namespace std;
class DataBase
{
public:
enum CFLAG { CLEAR = 1, NOT_CLEAR };
DataBase() { m_PGconn = NULL; m_pRes = NULL;}
~DataBase();
PGconn *PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout = 10);
PGconn *PgOpenDB(const char *pszDBName);
void PgCloseDB();
int PgResult(CFLAG flag);
PGconn *GetPgConn(){ return m_PGconn;}
PGresult *GetRes();
void SetRes(PGresult * v) {m_pRes = v;}
int GetCmdTuples();
int GetNoTuples();
int GetNoFields();
int GetResultCode() { return m_ResultCode; }
char *GetValue(int tuple, int field);
void PgClear();
int PgDoExec(string &strQuery);
int PgDoExec(char *pszQuery);
int PgDoExec(char *pszQuery, CFLAG flag);
int PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag = NOT_CLEAR);
int PgEscapeString(char *to, const char *from, size_t length);
string &GetErrorMessage();
private:
int m_ResultCode;
PGconn *m_PGconn;
PGresult *m_pRes;
string m_ErrorMessage;
};
#endif // ~__DATABASE_H__
+119
View File
@@ -0,0 +1,119 @@
#include "GarbageCleanThread.h"
#include "Logger.h"
#include <errno.h>
#include <string.h>
#include <time.h>
// Expire 처리를 위한 시간( sec )
// - vmasd 정보는 6 sec 마다 전송되므로... 기존에도 20 sec expire 처리했음.
// - csagentd 는 4 sec 마다 전송하지만.. tcp 세션(timeout 6 sec)이라.. 정상적인 상황이라면 정리할 필요 없음.
#define CLIENT_SHM_EXPIRE_TIME 20
// 작업 수행 주기 ( sec )
// - 빠른 판단으로 clean 처리를 해야.. FHS, RCDB 에 잘못된 정보가 전달되지 않음
// - 2018-02-22 #31703 일감에서 각 FHS 에서 정보 수신 timeout 발생시 대기가 아닌 세션을 종료하도록 수정
// 이로 인해 기존과 같이 빠른 garbage 처리가 필요하지 않아.. 동작 주기를 2 sec -> 4 sec 로 수정.
#define CLEAN_JOB_INTERVAL 4
// 생성자
CGarbageCleanThread::CGarbageCleanThread( CShmManager * pShmManager )
: m_pShmManager( pShmManager )
{
// Thread handle 초기화
m_threadHandle = 0;
}
// 소멸자
CGarbageCleanThread::~CGarbageCleanThread()
{
// Thread 가 동작 중인 경우 Thread 동작 정지 처리.
if( m_threadHandle != 0 )
{
pthread_cancel( m_threadHandle );
}
}
// Thread 를 생성하여 cache 작업 수행
bool CGarbageCleanThread::Start( void )
{
int nResult = pthread_create( &m_threadHandle, NULL, CGarbageCleanThread::threadFunc, this );
if( nResult != 0 )
{
// Thread 생성 실패시...
int errorNum = errno;
LOG( LERR, "GarbageCleanThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// Thread 함수
void * CGarbageCleanThread::threadFunc( void * arg )
{
CGarbageCleanThread * pObject = reinterpret_cast<CGarbageCleanThread *>( arg );
pthread_detach( pthread_self() );
// 주기적으로 공유메모리 clean 작업 수행
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 지정된 주기마다...
sleep( CLEAN_JOB_INTERVAL );
}
// Thread 종료시 Handle 초기화
pObject->m_threadHandle = 0;
return NULL;
}
// 실제 공유메모리 clean 처리를 담당하는 함수.
void CGarbageCleanThread::Execute()
{
// expire 시간 정보
time_t expireTime;
// 현재 시간 정보 추출.
// - 시간 동기화 오차보정 관련 방어를 위해 clock_gettime(CLOCK_MONOTONIC) 로 추출된 정보와
// - 공유메모리 service.update_time_clock_sec 항목을 비교토록 처리.
struct timespec currentClock;
memset( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
// expire 처리할 시간 계산.
if( currentClock.tv_sec <= CLIENT_SHM_EXPIRE_TIME )
{
// 추출된 시간 정보가 너무 작은 경우...
// - 이럴 경우는 거의 없지만.. 혹시나 해서...
LOG( LWAR, "GarbageCleanThread: expire clock value[%lu] too low. clean job skip.", currentClock.tv_sec );
return;
}
else
{
int result;
expireTime = currentClock.tv_sec - CLIENT_SHM_EXPIRE_TIME;
//_LOG( LDBG, "GarbageCleanThread: expire clock value[%lu]", expireTime );
// 공유메모리에 expire 처리 요청.
result= m_pShmManager->ExpireClient( expireTime );
if( result > 0 )
{
_LOG( LINF, "GarbageCleanThread: client expire count[%d] expire_clock[%lu]", result, expireTime );
}
}
return;
}
+66
View File
@@ -0,0 +1,66 @@
/****************************************************************************
shared memory garbage client info clean thread class
-----------------------------------------
begin : 2015/07/23
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __GARBAGE_CLEAN_THREAD_H__
#define __GARBAGE_CLEAN_THREAD_H__
#include "ShmDefine.h"
#include "ShmManager.h"
#include <pthread.h>
#include <unistd.h>
// Shared memory 상의 garbage client 정보를 확인하여 정리 작업을 수행하기 위한 Thread class
// - vmasd 종료, csagentd 의 이상 동작 등으로 인해
// - 공유메모리에 저장된 client 정보가 갱신되지 않고 오랫동안 유지되는 경우...
// - 잘못된 FHS 정보가 FHS, RCDB 상에 전달될 수 있으므로....
// - 이를 방지하기 위해 주기적으로 공유메모리의 client 정보를 확인하여... 유효하지 않은 정보는 정리 처리해 준다.
class CGarbageCleanThread
{
public:
// 생성 및 소멸자.
CGarbageCleanThread( CShmManager * pShmManager );
~CGarbageCleanThread();
// Thread 를 생성하여 작업 수행
bool Start( void );
private:
// 공유메모리 접근을 위한 inferface 객체 (생성자를 통해 전달받음)
CShmManager * m_pShmManager;
// 쓰레드 핸들
pthread_t m_threadHandle;
private:
// Thread function....
static void * threadFunc( void * arg );
// 실제 작업 처리를 담당하는 함수.
void Execute( void );
};
#endif /* __GARBAGE_CLEAN_THREAD_H__ */
+121
View File
@@ -0,0 +1,121 @@
#include "GmsSendThread.h"
#include "Logger.h"
#include <unistd.h>
#include <errno.h>
#include <string.h>
// GMS 전송 주기 (sec)
// rc_vrcd : 8 sec
// rc_statd : 10 sec ( sqlite 의 SQLITE_BUSY 발생 최소화 목적 )
// rc_mond : 10 sec ( FHS csagentd 로 부터 10 sec 단위로 시스템 정보를 수신하므로.. )
#define GMS_SEND_INTERVAL 10
// 생성자..
CGmsSendThread::CGmsSendThread( CShmManager * pShmManager )
: m_pShmManager( pShmManager)
{
// Thread Handle 초기화
m_threadHandle = 0;
// 저장 객체 초기화 및 빠른 처리를 위한 예약 처리.
m_vecSystemInfo.clear();
// 1024 개 까지 저장가능토록... (640 * 1024 = 640 KByte)
m_vecSystemInfo.reserve( MAX_CLIENT_COUNT );
}
// 소멸자..
CGmsSendThread::~CGmsSendThread()
{
// Thread 가 동작 중인 경우 Thread 동작 정지 처리.
if( m_threadHandle != 0 )
pthread_cancel(m_threadHandle);
}
// Thread 를 생성하여 작업을 시작한다...
bool CGmsSendThread::Start()
{
int nResult = pthread_create( &m_threadHandle, NULL, CGmsSendThread::threadFunc, this );
if( nResult != 0 )
{
// Thread 생성 실패시...
int errorNum = errno;
LOG( LERR, "GmsSendThread: Thread create failed.[%d][%s]", errorNum, strerror(errorNum) );
return false;
}
return true;
}
// Thread 함수...
void * CGmsSendThread::threadFunc( void * arg )
{
CGmsSendThread * pObject = reinterpret_cast<CGmsSendThread *>(arg);
pthread_detach( pthread_self() );
// 초기 기동시...FHS 상태 정보가 아직 수집되지 않은 상태이므로...
// 잠시 sleep 했다가.. 작업을 시작한다.
sleep( GMS_SEND_INTERVAL );
// 주기적으로 GMS 에 전송 처리
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
sleep( GMS_SEND_INTERVAL );
}
// Thread 종료시 Handle 초기화
pObject->m_threadHandle = 0;
return NULL;
}
// 실제 GMS 로 RC 상태 정보 전송 작업을 수행한다...
void CGmsSendThread::Execute()
{
// 공유메모리의 RC 정보를 멤버변수 m_vecSystemInfo 에 저장 처리.
m_vecSystemInfo.clear();
m_pShmManager->GetGmsSendList( m_vecSystemInfo );
// 멤버 변수 m_vecSystemInfo 에 저장된 RC 정보를 GMS 로 전송 처리.
// => 전송할 정보가 없을 경우에는 전송하지 않는다.
// => GMS Web 상에는 이전에 수신된 정보로 표시됨.
if( m_vecSystemInfo.empty() == false )
{
/*
// Debug 목적..
for( unsigned int i = 0; i < m_vecSystemInfo.size(); ++i )
{
_LOG( LDBG, "GmsSendThread: vector result [%s][%lu] [%d][%lu][%u] "
, m_vecSystemInfo[i].domain, m_vecSystemInfo[i].stamp
, m_vecSystemInfo[i].manual_disable, m_vecSystemInfo[i].free, m_vecSystemInfo[i].traffic_ob );
}
*/
// GMS 로 전송 처리..
// 전송 결과는 본 Thread 에서는 중요하지 않으므로.. 반환값 검사 안함.
unsigned int nSendSize = sizeof( struct PacketDataGms ) * m_vecSystemInfo.size();
m_socketGms.SendData( m_vecSystemInfo.size(), (char *)( &m_vecSystemInfo[0] ), nSendSize );
// 로깅
_LOG( LINF, "GmsSendThread: GMS send data. count [%zu]", m_vecSystemInfo.size() );
}
else
{
// GMS 로 전송할 Data 가 없는 경우.. 로깅 처리..
_LOG( LINF, "GmsSendThread: GMS send data empty. not send.." );
}
return;
}
+69
View File
@@ -0,0 +1,69 @@
/****************************************************************************
GMS Send Thread Class
-----------------------------------------
begin : 2015/07/27
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 __GMS_SEND_THREAD_H__
#define __GMS_SEND_THREAD_H__
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <vector>
#include "ProtocolGms.h"
#include "SocketGms.h"
#include "ShmManager.h"
/// @brief CGmsSendThread
/// 본 클래스는 Thread 클래스로서...
/// FHS 등의 상태 정보를 주기적으로 GMS 에 전송 처리한다.
class CGmsSendThread
{
public:
// 생성 및 소멸자.
CGmsSendThread( CShmManager * pShmManager );
~CGmsSendThread();
// GMS 로 전송 작업을 시작
bool Start( void );
private:
// Thread function....
static void * threadFunc( void * arg );
// 실제 GMS 로 정보 전송을 담당하는 함수...
void Execute( void );
private:
// 쓰레드 핸들
pthread_t m_threadHandle;
// 공유메모리 접근을 위한 inferface 객체 (생성자를 통해 전달받음)
CShmManager * m_pShmManager;
// FHS 등의 상태 정보를 저장할 Vector 객체
std::vector<struct PacketDataGms> m_vecSystemInfo;
// GMS 로 Data 전송을 담당할 Socket 처리 Class.
CSocketGms m_socketGms;
};
#endif /* __GMS_SEND_THREAD_H__ */
File diff suppressed because it is too large Load Diff
+92
View File
@@ -0,0 +1,92 @@
/****************************************************************************
Main.h for rc_mond
-----------------------------------------
begin : 2015/06/02
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 __RC_MOND_MAIN_H__
#define __RC_MOND_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 );
/// @brief vmasd 가 전송하는 시스템 정보 수신을 담당할 Worker 프로세스 생성.
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerRecvVmasd();
/// @brief csagentd 가 전송하는 FHS system 상태 정보 수신을 담당할 Worker 프로세스 생성.
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerRecvSystem();
/// @brief csagentd 가 전송하는 FHS check 상태 정보 수신을 담당할 Worker 프로세스 생성.
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerRecvCheck();
/// @brief 각 Client 의 요청을 수신하여.. Alive FHS 목록 등의 정보 전달을 담당할 Worker 프로세스 생성.
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerSendInfo();
/// @brief 각 FHS 정보의 GMS 전송, Top FHS 정보의 RCDB 업데이트 등을 수행할 Worker 프로세스 생성.
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerControl();
// FHS vmasd 로 부터 Broadcast 되는 FHS 상태 정보 수신을 위한 UDP Receive Socket 생성
bool MakeVmasdRecvSocket( void );
// FHS csagentd 가 전송하는 시스템 상태 정보 수신을 위한 TCP listen socket 생성
bool MakeSystemRecvSocket( void );
// FHS csagentd 가 전송하는 서비스 상태 체크 정보 수신을 위한 TCP listen socket 생성
bool MakeCheckRecvSocket( void );
// fimngd 등과 같은 외부 모듈과 통신을 통해 정보를 전달하기 위한 TCP listen socket 생성
bool MakeInfoSendSocket( void );
#ifdef __cplusplus
}
#endif
#endif /* __RC_MOND_MAIN_H__ */
+86
View File
@@ -0,0 +1,86 @@
#****************************************************************************
# Makefile for rc_mond
# -----------------------------------------
#
# begin : 2015/06/02
# 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_mond
REVISION = 1440
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
DEFAULT_CONFIG_FILE = /user/service/etc/rcts.conf
MAX_CLIENT_COUNT = 1024
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)\" -DMAX_CLIENT_COUNT=$(MAX_CLIENT_COUNT)
# Application Enviroment
APP = $(PROG_NAME) rc_show
DIR_INCLUDE = -I../lib -I/user/db/pgsql/include
DIR_LIB = -L../lib
OBJ = ProcessConfig.o ShmManager.o ShmClientInfo.o SocketCsagentd.o \
AliveFhsCacheThread.o SocketSendInfo.o \
GarbageCleanThread.o SocketGms.o GmsSendThread.o Database.o RcdbInfo.o TopFhsUpdateThread.o \
WorkerRecvVmasd.o WorkerRecvSystem.o WorkerRecvCheck.o WorkerSendInfo.o WorkerControl.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)
rc_show: ShmMonitor.o
$(CC) -o $@ $^
clean:
-rm -f *.o core.* *.out *.log
-rm -f $(APP)
sync
install : $(APP)
-cp $(PROG_NAME) $(INSTALL_BIN)/$(PROG_NAME)
-cp rc_show $(INSTALL_BIN)/rc_show
sync
# End of Makefile
+350
View File
@@ -0,0 +1,350 @@
#include "ProcessConfig.h"
#include <stdlib.h>
#include "Logger.h"
// static 변수 초기화
CProcessConfig * CProcessConfig::m_pInstance = NULL;
// 생성자
CProcessConfig::CProcessConfig()
{
// default 값 설정.
// FHS_STATUS_BROADCAST_UDP_PORT
// 설정값 0 이거나 미 존재시...해당 수신 프로세스 기동하지 않도록 하기 위함.
m_nFhsBroadcastRecvPort = 0;
// REPORT_MEMORY_MAX_USAGE
// 설정값이 0 또는 미존재시... 해당 기능을 사용하지 않도록 하기 위해 default 0 값 설정 처리
m_nReportMemoryMaxUsage = 0;
}
// 소멸자
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_STATUS_BROADCAST_UDP_PORT
// - vmasd 의 FHS 상태 정보 수신을 위한 Port.
// - 생략 또는 0 으로 설정 가능.
if( GetConfigValue( conf, strProgramName, "FHS_STATUS_BROADCAST_UDP_PORT", strValue, strErrorMessage ) == false )
{
// 해당 항목이 존재하지 않는 경우.. 0 으로 설정 처리.
m_nFhsBroadcastRecvPort = 0;
}
else
{
m_nFhsBroadcastRecvPort = atoi( strValue.c_str() );
// 0 으로 설정된 경우.. 미사용 표시이므로.. Range 검사시 허용 처리함.
if( m_nFhsBroadcastRecvPort < 0 || m_nFhsBroadcastRecvPort > 65535 )
{
strErrorMessage = "Config [FHS_STATUS_BROADCAST_UDP_PORT] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// PORT_TCP_LISTEN_SYSTEM_STAT
if( GetConfigValue( conf, strProgramName, "PORT_TCP_LISTEN_SYSTEM_STAT", strValue, strErrorMessage ) == false )
return false;
else
{
m_nTcpListenPortSystemStat = atoi( strValue.c_str() );
if( m_nTcpListenPortSystemStat <= 0 || m_nTcpListenPortSystemStat > 65535 )
{
strErrorMessage = "Config [PORT_TCP_LISTEN_SYSTEM_STAT] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// PORT_TCP_LISTEN_CHECK_STAT
if( GetConfigValue( conf, strProgramName, "PORT_TCP_LISTEN_CHECK_STAT", strValue, strErrorMessage ) == false )
return false;
else
{
m_nTcpListenPortCheckStat = atoi( strValue.c_str() );
if( m_nTcpListenPortCheckStat <= 0 || m_nTcpListenPortCheckStat > 65535 )
{
strErrorMessage = "Config [PORT_TCP_LISTEN_CHECK_STAT] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// PORT_TCP_LISTEN_SEND_INFO
if( GetConfigValue( conf, strProgramName, "PORT_TCP_LISTEN_SEND_INFO", strValue, strErrorMessage ) == false )
return false;
else
{
m_nTcpListenPortSendInfo = atoi( strValue.c_str() );
if( m_nTcpListenPortSendInfo <= 0 || m_nTcpListenPortSendInfo > 65535 )
{
strErrorMessage = "Config [PORT_TCP_LISTEN_SEND_INFO] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// GMS_SERVER
if( GetConfigValue( conf, strProgramName, "GMS_SERVER", strValue, strErrorMessage ) == false )
return false;
else
{
m_strGmsServer = strValue;
strValue.clear();
}
// GMS_PORT_STAT -> 각 RC 의 상태 정보를 GMS 로 전송하기 위한 통신 port
if( GetConfigValue( conf, strProgramName, "GMS_PORT_STAT", strValue, strErrorMessage ) == false )
return false;
else
{
m_nGmsPortStat = atoi( strValue.c_str() );
if( m_nGmsPortStat <= 0 || m_nGmsPortStat > 65535 )
{
strErrorMessage = "Config [GMS_PORT_STAT] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// STORAGE_MAX_USAGE_TO_DISABLE
if( GetConfigValue( conf, strProgramName, "STORAGE_MAX_USAGE_TO_DISABLE", strValue, strErrorMessage ) == false )
return false;
else
{
m_nStorageMaxUsageToDisable = atoi( strValue.c_str() );
if( m_nStorageMaxUsageToDisable < 0 || m_nStorageMaxUsageToDisable > 100 )
{
strErrorMessage = "Config [STORAGE_MAX_USAGE_TO_DISABLE] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// REPORT_MEMORY_MAX_USAGE
// - FHS memory usage 값이 지정된 값 이상을 경우 report 로깅 처리
// - 생략 또는 0 으로 설정 가능
if( GetConfigValue( conf, strProgramName, "REPORT_MEMORY_MAX_USAGE", strValue, strErrorMessage ) == false )
{
// 해당 항목이 존재하지 않는 경우.. 0 으로 설정 처리
m_nReportMemoryMaxUsage = 0 ;
}
else
{
m_nReportMemoryMaxUsage = atoi( strValue.c_str() );
// 0 인 경우 미사용 설정이므르.. Range 검사시 허용 처리
if( m_nReportMemoryMaxUsage < 0 || m_nReportMemoryMaxUsage > 100 )
{
strErrorMessage = "Config [REPORT_MEMORY_MAX_USAGE] 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;
}
+146
View File
@@ -0,0 +1,146 @@
/****************************************************************************
rc_mond config processing module
-----------------------------------------
begin : 2015/06/03
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.
// FHS 의 vmasd 에서 Broadcasting 하는 UDP Data 수신을 위한 UDP Port = 13201
// 0 일 경우.. Broadcasting recv 기능을 사용하지 않는 것으로 판단한다.
unsigned int m_nFhsBroadcastRecvPort;
// 프로세스 설정 정보
// FHS csagentd 가 전송하는 FHS system 상태 정보 수신을 위한 TCP port = 14003
unsigned int m_nTcpListenPortSystemStat;
// FHS csagentd 가 전송하는 FHS service check 상태 정보 수신을 위한 TCP port = 14004
unsigned int m_nTcpListenPortCheckStat;
// fimngd 등의 Client 요청에 대해 Alive FHS 목록 등의 결과 전달을 위한 listen 할 TCP port = 14005
unsigned int m_nTcpListenPortSendInfo;
std::string m_strGmsServer; // GMS new_sh_statusd server domain
unsigned int m_nGmsPortStat; // GMS new_sh_statusd server tcp port
// 업로드 방지 처리 관련 Storage 최대 사용량 설정값. (%)
int m_nStorageMaxUsageToDisable;
// 2018-01-11 NEW huibong 메모리 사용량 초과시 report 기능 추가 (#31360, #31679)
// - FHS memory 사용량(%) check 를 위한 설정값 (%)
int m_nReportMemoryMaxUsage;
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 GetFhsBroadcastRecvPort() { return m_nFhsBroadcastRecvPort; }
unsigned int GetPortRecvSystem() { return m_nTcpListenPortSystemStat; }
unsigned int GetPortRecvCheck() { return m_nTcpListenPortCheckStat; }
unsigned int GetPortSendInfo() { return m_nTcpListenPortSendInfo; }
const char * GetGmsServer() { return m_strGmsServer.c_str(); }
unsigned int GetGmsPortStat() { return m_nGmsPortStat; }
int GetStorageMaxUsageToDisable() { return m_nStorageMaxUsageToDisable; }
int GetReportMemoryMaxUsage() { return m_nReportMemoryMaxUsage; }
};
#endif /* __PROCESS_CONFIG_H__ */
+128
View File
@@ -0,0 +1,128 @@
/****************************************************************************
csagentd 와의 통신 관련 protocol 정의
-----------------------------------------
begin : 2015/07/02
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 __PROTOCOL_CSAGENTD_H__
#define __PROTOCOL_CSAGENTD_H__
#include <unistd.h>
#include <sys/types.h>
#include <stdint.h>
// Packet Header stx 코드
#define HEADER_STX_CODE_CSAGENTD 0x05
// Packet Header type 구분코드
#define HEADER_TYPE_REQUEST 0x00
#define HEADER_TYPE_RESPONSE 0x01
// Packet Header command 구분 코드.
#define COMMAND_TRANSFER_SYSTEM 0x01 // system 수집 정보 전송.
#define COMMAND_TRANSFER_SERVICE 0x02 // service 상태 check 정보 전송.
// 기타 정보
#define LENGTH_FIELD_SIZE 4 // 가변데이터 형식 사용시 Length 필드의 메모리 크기 (unsigned int)
// FHS csagentd 와 통신시 사용할 통신 Header
// - 계산상 크기는 16Byte 이지만...
// - 통신상 sizeof() : 20Byte ( 64Bit OS )
struct PacketHeaderCsagentd {
char stx; // Packet 유효성 관리 코드
char type; // Request or Response 여부 ( 0x00: Request, 0x01: Response )
char command[4]; // Command Code ( 4 Byte) : 0th csagent-rc_mond 만 사용.
char result[4]; // Result Code ( 4 Byte )
unsigned int data_length; // Packet Data 부분의 길이값 ( Network Byte Order 사용)
// 2015-05-15 huibong
// Protocol version 정보 추가로 인해... extend_code 2Byte 중 1Byte 사용.
//char extend_code[2]; // 확장 및 Padding bits ( 2 Byte )
char protocol_version; // Packet Data 의 Protocol Version 정보. (1 Byte)
char extend_code; // 확장 및 Padding bits ( 1 Byte )
};
////////////////////////////////////////////
// RC ID 최대 길이.
#define CSAGENTD_RC_ID_MAX_LENGTH 31
// hostname 최대 길이.
#define CSAGENTD_HOST_NAME_MAX_LENGTH 63
// FHS csagentd 에서 수신될 system 수집 data 구조체 protocol version 중 처리 가능한 최대 Version 정보.
// - 최초 버전이므로 0x00 으로 설정.
#define PARSE_MAX_PROTOCOL_VERSION_SYSTEM 0x00
// FHS csagentd 에서 수신될 system 수집정보 data 구조체
// - 전송할 항목이 많아... struct 형식으로 전송 처리.
// - protocol version : 0x00
// - 공유메모리 SystemInfo 구조체를 사용하도록 처리할 수 있지만...
// -- 공유메모리 구조 변경시.. 통신 오류 발생 가능..
// -- 따라서 복잡하더라고.. 공유메모리 구조체와 통신 구조체를 별도로 분리 처리함.
struct PacketDataSystem_V00 {
char rcid[CSAGENTD_RC_ID_MAX_LENGTH + 1]; // RCID 정보, 최대 31 자까지만 허용.
char hostname[CSAGENTD_HOST_NAME_MAX_LENGTH + 1]; // hostname, key 값으로 사용, 최대 63자까지만 허용.
double load_avg[3]; // load average.
uint64_t memory_total; // Total memory size (byte 단위)
uint64_t memory_free; // Free memory size (byte 단위)
int memory_usage; // memory 사용률 (%)
uint64_t swap_total; // Total swap size (byte 단위)
uint64_t swap_used; // Used swap size (byte 단위)
int swap_usage; // swap 사용률 (%)
uint64_t storage_total; // Total Storage size (byte 단위)
uint64_t storage_avail; // Available Storage size (byte 단위)
int storage_max_usage; // Storage 중 최대 사용률 (%)
uint64_t disk_max_queue; // Disk 상태 정보 중 Max Queue Length
double disk_max_busy; // Disk 상태 정보 중 Max busy(%)
uint64_t network_packet_in; // Network inbound packet count (단위 시간내의 count)
uint64_t network_packet_out; // Network outbound packet count (단위 시간내의 count)
uint64_t network_traffic_in; // Network inbound 평균 traffic (Byte/sec)
uint64_t network_traffic_out; // Network outbound 평균 traffic (Byte/sec)
unsigned int tcp_total; // TCP 세션 total count
unsigned int tcp_80; // TCP 세션 중 local 80 port 를 사용하는 세션 count
unsigned int tcp_80_establish; // TCP 세션 중 local 80 port 를 사용하고.. ESTABLISH 상태인 세션 count
};
// FHS csagentd 에서 수신될 service 상태 data 구조체 protocol version 중 처리 가능한 최대 version 정보.
// - 최초 버전이므로 0x00 으로 설정.
#define PARSE_MAX_PROTOCOL_VERSION_SERVICE 0x00
// FHS csagentd 에서 수신될 service 상태 data 구조체
// - protocol version : 0x00
struct PacketDataService_V00 {
char rcid[CSAGENTD_RC_ID_MAX_LENGTH + 1]; // RCID 정보, 최대 31 자까지만 허용.
char hostname[CSAGENTD_HOST_NAME_MAX_LENGTH + 1]; // hostname, key 값으로 사용, 최대 63자까지만 허용.
int service_stat; // Service 상태 정보, csagentd ShmDefine.h 정의 참고.
int storage_max_usage; // Storage 최대 사용률(%), csagentd ShmDefine.h 정의 참고.
};
#endif /* __PROTOCOL_CSAGENTD_H__ */
+91
View File
@@ -0,0 +1,91 @@
/****************************************************************************
GMS 와의 통신 관련 protocol 정의
-----------------------------------------
begin : 2015/07/07
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 __PROTOCOL_GMS_H__
#define __PROTOCOL_GMS_H__
#include <unistd.h>
#include <sys/types.h>
#include <stdint.h>
// GMS 로 전송시 Header 의 ulmode 값
#define GMS_MSG_MODE_VRCD_STATUSD_RTS_STATUS 0x0a01
// GMS 로 전송시 Header 의 ulResult 값
#define GMS_OK_ALL_PASSED 0x1000
// GMS 로 Data 전송 관련 Header 구조체..
// 본 Header 의 크기는 32bit, 64Bit OS 모두 동일하게 320 Byte 임.
struct PacketHeaderGms {
char szTransactionID[52];
uint32_t ulMode; // Network Byte Order
char szIssuer[128];
char szServiceID[128];
uint32_t ulResult; // Network Byte Order
uint32_t ulBodyLength; // Network Byte Order
};
// GMS 로 전달할 각 장비의 상태 정보를 저장하기 위한 구조체...
// - GMS 장비가 64 Bit 인 경우에만 사용. ( 32Bit 는 현재 사용하지 않지만.. GMS 가 32Bit 인 경우.. time_t 형 size 문제로 통신 오류 발생함. )
struct PacketDataGms {
time_t stamp; // stamp 값은 broadcast 를 수신한 시간정보 값임.
// GMS 가 64Bit 장비인 경우 time_t 의 size 가 8 Byte, 만약 GMS 장비가 32 Bit 인 경우.. time_t 의 size 가 4Byte 이므로...
// 아래 코드를 사용할 것
// #pragma pack(4)
// int stamp;
// #pragma pack()
char domain[512]; // vmasd.conf에 설정된 SERVER_NAME=sb-01-fhs001.solbox.com 정보 사용
// 해당 정보는 FHS 패키징 설치시 hostname 정보를 추출하여 자동 설정 처리하므로..
// hostname 정보와 동일하다.
uint64_t total; // 저장 경로로 지정된 Mount 의 총 Disk Size ( 각 mount statfs 의 f_blocks * f_bsize 합산 결과 )
uint64_t free; // 저장 경로로 지정된 Mount 의 총 Disk Free Size ( 각 mount statfs 의 f_blocks * f_bavail 합산 결과 )
// 본 total, free 값은 /stg/node0.disable 설정에는 영향받지 않음.
int manual_disable; // 2 : fsck_ufs 프로세스가 동작 중인 경우..
// 1 : /user/service/vmasd.disable 파일이 존재할 경우..
// .disable 설정이 되지 않은 유효 Mount 중.. 가장 큰 Free Disk 용량(f_bfree) 이 0 인 경우...
// 0 : 그 외..
int upload_level; // 본 구조체 항목 중 (disk_backlog/10) + (connection_established/50) 의 계산 값... ( vmasd 소스 참고)
int disk_backlog; // 저장 경로 Mount 들의 Disk Queue 평균 대기 transaction 수
// 세부 내역은 devstat_compute_statistics 의 DSM_QUEUE_LENGTH 참고
int disk_percent; // 저장 경로 Mount 들의 평균 Busy 율 ( devstat_compute_statistics 의 DSM_BUSY_PCT 참고 )
int connection_established; // TCP 80 Port 에 연결된 세션 수..
int connection_total; // TCP 80 전체 세션 수.
uint32_t traffic_ip; // 평균 inbound packet count
uint32_t traffic_ib; // 평균 inbound traffic ( byte/s )
uint32_t traffic_op; // 평균 outbound packet count
uint32_t traffic_ob; // 평균 outbound traffic ( byte/s )
char hostname[64]; // 장비 hostname (gethostname 함수로 추출)
// domain[512] 와 동일 data 임.
// 만약 domain 정보와 hostname 정보가 다른 경우는 패키징 설치 후 장비 hostname 을 임의로 변경한 경우임.
};
#endif /* __PROTOCOL_GMS_H__ */
+96
View File
@@ -0,0 +1,96 @@
/****************************************************************************
SendInfo Worker 프로세스의 통신 관련 protocol 정의
-----------------------------------------
begin : 2015/07/16
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 __PROTOCOL_SEND_INFO_H__
#define __PROTOCOL_SEND_INFO_H__
#include <unistd.h>
#include <sys/types.h>
#include <stdint.h>
// Packet Header stx 코드
#define HEADER_STX_CODE_SEND_INFO 0x05
// Packet Header type 구분코드
#define HEADER_TYPE_REQUEST 0x00
#define HEADER_TYPE_RESPONSE 0x01
// Packet Header command 구분 코드.
// 현재는 0th 미사용, 1th 항목만 사용.
#define COMMAND_ALIVE_FHS 0x01 // fimngd 의 Alive FHS 목록 요청 (0x00 0x01 0x00 0x00)
// Packet Header result 구분 코드.
// - fimngd 와 통신시 0th, 1th, 2th 미사용, 3th 만 사용.
#define RESULT_ALIVE_FHS_OK 0x00 // Alive FHS 목록 요청에 대해 정상 결과 전송시 (0x00 0x00 0x00 0x00)
#define RESULT_ALIVE_FHS_ERROR 0x01 // Alive FHS 목록 요청에 대해 오류 결과 전송시 (0x00 0x00 0x00 0x01)
#define RESULT_ALIVE_FHS_SKIP 0x02 // Alive FHS 목록 요청에 관련 목록이 아직 유효하지 않은 경우.. 대기 의미로 SKIP 결과 전달. (0x00 0x00 0x00 0x02)
// 기타 정보
#define LENGTH_FIELD_SIZE 4 // 가변데이터 형식 사용시 Length 필드의 메모리 크기 (unsigned int)
// SendInfo Worker 에서 통신시 사용할 통신 Header
// - 계산상 크기는 16Byte 이지만...
// - 통신상 sizeof() : 20Byte ( 64Bit OS )
struct PacketHeaderSendInfo {
char stx; // Packet 유효성 관리 코드
char type; // Request or Response 여부 ( 0x00: Request, 0x01: Response )
char command[4]; // Command Code ( 4 Byte) : 0th 미사용, 1th fimngd-rc_mond 에서 사용)
char result[4]; // Result Code ( 4 Byte )
unsigned int data_length; // Packet Data 부분의 길이값 ( Network Byte Order 사용)
// 2015-05-15 huibong
// Protocol version 정보 추가로 인해... extend_code 2Byte 중 1Byte 사용.
//char extend_code[2]; // 확장 및 Padding bits ( 2 Byte )
char protocol_version; // Packet Data 의 Protocol Version 정보. (1 Byte)
char extend_code; // 확장 및 Padding bits ( 1 Byte )
};
////////////////////////////////////////////
// Alive FHS 요청 및 결과 전송 관련 Data 항목 및 구조체.
// RC ID 최대 길이.
#define FIMNGD_RC_ID_MAX_LENGTH 31
// hostname 최대 길이.
#define FIMNGD_HOST_NAME_MAX_LENGTH 63
// FHS fimngd 와 통신 관련 Alive FHS 요청, 응답 처리 protocol version 중 처리 가능한 최대 Version 정보.
// - 최초 버전이므로 0x00 으로 설정.
#define PARSE_MAX_PROTOCOL_VERSION_ALIVE_FHS 0x00
// FHS fimngd 에서 수신할 Alive FHS 목록 요청 관련 data 구조체
struct PacketDataRequestAliveFhs_V00 {
char rcid[FIMNGD_RC_ID_MAX_LENGTH + 1]; // RCID 정보, 최대 31 자까지만 허용.
char hostname[FIMNGD_HOST_NAME_MAX_LENGTH + 1]; // hostname, 최대 63자까지만 허용.
};
// FHS fimngd 에 전송할 Alive FHS 목록 관련 개별 항목 구조체.
struct PacketDataResponseAliveFhs_V00 {
char hostname[FIMNGD_HOST_NAME_MAX_LENGTH + 1]; // hostname, 최대 63자까지만 허용.
int service_stat; // Service 상태 code. ( 정상 or Read only 장비만.)
};
#endif /* __PROTOCOL_SEND_INFO_H__ */
+92
View File
@@ -0,0 +1,92 @@
/****************************************************************************
Broadcast Message Header for rc_mond
-----------------------------------------
begin : 2015/06/05
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 __PROTOCOL_VMASD_H__
#define __PROTOCOL_VMASD_H__
#include <unistd.h>
#include <stdint.h>
// 본 Header 파일은 FHS 의 vmasd 가 전송하는 UDP Broadcasting Message Protocol 을 정의하기 위한 header 이다.
// FHS vmasd 에 정의된 Protocol 형식이 향후 변경될 수 있으므로.. 다음과 같이 특정하여 처리한다.
// sizeof( struct vmasd_broadcast_message_header ) => 352 (64Bit OS)
struct vmasd_broadcast_message_header {
char szTransactionID[52]; // 20130802181203-4ef48917f7-4381644055 ( 세션 ID 정보 )
uint32_t ulMode; // 256 (MODE 정보, network byte odrer, 0x0100 만 사용)
char szIssuer[128]; // vmasd (프로세스명)
char szServiceID[128]; // 미사용
char szRCID[32]; // bd16 (RC ID)
uint32_t ulResult; // 4096 (처리결과, network byte odrer)
uint32_t ulBodyLength; // 640 (Body Size , network byte odrer)
};
// vmasd 가 전송한 UDP Broadcast 메시지 중....
// vmasd_broadcast_message_header 구조체에서 ntohl(ulMode) == MSG_MODE_VMA_APACHE_DISKSIZE 정보만 처리한다.
#define MSG_MODE_VMA_APACHE_DISKSIZE 0x0100
// rc_vrcd share.h 에 정의된 struct disk_usage 구조체 및 vmasd 소스 참고함.
// 구조체 명은 변경 처리함.
// sizeof( struct vmasd_broadcast_message_body ) => 640 (64Bit OS)
// 각 항목에 대한 설명 내역은 vmasd 소스 Revision 0551 을 기준으로 한 분석 내역임.
struct vmasd_broadcast_message_body {
char domain[512]; // vmasd.conf에 설정된 SERVER_NAME=sb-01-fhs001.solbox.com 정보 사용
// 해당 정보는 FHS 패키징 설치시 hostname 정보를 추출하여 자동 설정 처리하므로..
// hostname 정보와 동일하다.
uint64_t total; // 저장 경로로 지정된 Mount 의 총 Disk Size ( 각 mount statfs 의 f_blocks * f_bsize 합산 결과 )
uint64_t free; // 저장 경로로 지정된 Mount 의 총 Disk Free Size ( 각 mount statfs 의 f_blocks * f_bavail 합산 결과 )
// 본 total, free 값은 /stg/node0.disable 설정에는 영향받지 않음.
int manual_disable; // 2 : fsck_ufs 프로세스가 동작 중인 경우..
// 1 : /user/service/vmasd.disable 파일이 존재할 경우..
// .disable 설정이 되지 않은 유효 Mount 중.. 가장 큰 Free Disk 용량(f_bfree) 이 0 인 경우...
// 0 : 그 외..
int upload_level; // 본 구조체 항목 중 (disk_backlog/10) + (connection_established/50) 의 계산 값... ( vmasd 소스 참고)
int disk_backlog; // 저장 경로 Mount의 Disk Queue 평균 대기 transaction 수
// 실제로는 평균이 아니라.. N개 mount 중 마지막 mount 의 추출 값임.
// 세부 내역은 devstat_compute_statistics 의 DSM_QUEUE_LENGTH 참고
int disk_percent; // 저장 경로 Mount 들의 평균 Busy 율 ( devstat_compute_statistics 의 DSM_BUSY_PCT 참고 )
int connection_established; // TCP 80 Port 사용, ESTABLISH 상태인 세션 수..
int connection_total; // TCP 80 Port 전체 세션 수.
uint32_t traffic_ip; // 평균 inbound packet count
uint32_t traffic_ib; // 평균 inbound traffic ( byte/s )
uint32_t traffic_op; // 평균 outbound packet count
uint32_t traffic_ob; // 평균 outbound traffic ( byte/s )
char hostname[64]; // 장비 hostname (gethostname 함수로 추출)
// domain[512] 와 동일 data 임.
// 만약 domain 정보와 hostname 정보가 다른 경우는 패키징 설치 후 장비 hostname 을 임의로 변경한 경우임.
uint64_t large_block; // 저장 경로로 지정된 Mount 중.... disable 설정(/stg/node0.disable)이 안되고..
// 가장 큰 Free Size 를 가지는 Mount 의 Disk Free 용량임.
// 본 값은 해당 FHS 에서 저장 가능한 가장 큰 Content Size 를 의미한다.
// 본 항목은 송신측인 vmasd 에만 정의되어 있으며.. 수신측인 rc_vrcd 에는 정의되어 있지 않음.
// 따라서.. GMS 로 본 항목은 전송하지 않는다. (전송하면 파싱 오류 발생 할 듯 )
};
#endif /* __PROTOCOL_VMASD_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__ */
+338
View File
@@ -0,0 +1,338 @@
#include "ShmClientInfo.h"
#include "Logger.h"
#include "ProcessConfig.h"
#include "ReportLog.h"
#include <string.h>
// 2018-02-22 CHG huibong FHS Disk busy 상황시 report 로깅을 위한 기준값
#define REPORT_MAX_DISK_QUEUE 10 // disk queue 값이 해당 정의 값 이상인 경우
#define REPORT_MAX_DISK_BUSY 90.0 // disk busy 값이 해당 정의 값 이상인 경우
// 생성자
CShmClientInfo::CShmClientInfo( struct ClientInfo * p )
: m_pClient( p )
{
// 서비스 상태 code 에 대한 문자열 정보를 저장하기 위한 멤버 변수 설정.
// - may 객체이므로.. key - value 형식으로 저장 처리
m_mapServiceStatString[SERVICE_STAT_NOT_DEFINE] = "Not Define";
m_mapServiceStatString[SERVICE_STAT_OK] = "OK";
m_mapServiceStatString[SERVICE_STAT_OUT] = "Out";
m_mapServiceStatString[SERVICE_STAT_DISABLE] = "Disable";
m_mapServiceStatString[SERVICE_STAT_RONLY] = "Read-only";
// NEW 2018-01-11 huibong 메모리 사용량 초과시 report 기능 추가 (#31360, #31679)
// check 여부 판단을 위한 변수 초기화
if( CProcessConfig::GetInstance()->GetReportMemoryMaxUsage() > 0 )
m_bReportMemoryMaxUsage = true;
else
m_bReportMemoryMaxUsage = false;
m_iShmSystemUpdateCount = 0;
}
// 소멸자
CShmClientInfo::~CShmClientInfo()
{
}
// Service 상태 정보에 대한 문자열 반환.
const char * CShmClientInfo::GetServiceStatString( const int & stat )
{
std::map< int, std::string >::iterator pos;
pos = m_mapServiceStatString.find( stat );
if( pos != m_mapServiceStatString.end() )
{
// 해당 항목 존재시
return pos->second.c_str();
}
else
{
// 해당 항목이 존재하지 않는 경우.
return m_mapServiceStatString[SERVICE_STAT_NOT_DEFINE].c_str();
}
}
// 현재 Service 상태 정보에 대한 문자열 반환.
const char * CShmClientInfo::GetServiceStatString()
{
std::map< int, std::string >::iterator pos;
pos = m_mapServiceStatString.find( m_pClient->service.service_stat);
if( pos != m_mapServiceStatString.end() )
{
// 해당 항목 존재시
return pos->second.c_str();
}
else
{
// 해당 항목이 존재하지 않는 경우.
return m_mapServiceStatString[SERVICE_STAT_NOT_DEFINE].c_str();
}
}
// 공유 메모리 상태가 유효하지 여부 판단.
bool CShmClientInfo::IsValid( const char * pClientAddress ) const
{
if( m_pClient == NULL )
return false;
else
{
// 해당 공유메모리가 미사용 중으로 마킹된 경우...
if( m_pClient->use == 0 )
return false;
// 해당 공유메모리의 client IP 가 서로 다른 경우...
if( strcmp( pClientAddress, m_pClient->client_ip ) != 0 )
return false;
}
return true;
}
// serviceinfo 정보를 공유메모리에 저장 처리.
// return false : 공유메모리가 유효하지 않은 경우...
bool CShmClientInfo::UpdateServiceInfo( std::string & strHostname, int iServiceStat )
{
// 공유메모리가 유효하지 않은 경우..
if( m_pClient == NULL || m_pClient->use == 0 )
return false;
int iPreviousServiceStat = 0;
// 현재 시간 정보 추출.
time_t currentTime = time( 0 );
struct timespec currentClock;
memset( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
// 정보 설정.
if( m_pClient->valid == 1 )
{
// hostname 정보 설정.
// - 기존 값과 비교하여 변경된 경우.. 관련 내역 로깅.
if( strcmp( m_pClient->hostname, strHostname.c_str() ) != 0 )
{
LOG( LWAR, "client [%s] hostname changed [%s] -> [%s].", m_pClient->client_ip, m_pClient->hostname, strHostname.c_str() );
// hostname 정보 복사
strncpy( m_pClient->hostname, strHostname.c_str(), HOST_NAME_LENGTH );
}
// 이전 서비스 상태 정보 저장
iPreviousServiceStat = m_pClient->service.service_stat;
// 서비스 상태 정보 변경 여부 관련 내역 로깅
if( iPreviousServiceStat != iServiceStat )
{
_LOG( LINF, "client[%s] hostname[%s] service stat changed. [%s] -> [%s]."
, m_pClient->client_ip, m_pClient->hostname, GetServiceStatString( iPreviousServiceStat ), GetServiceStatString( iServiceStat ) );
// 서비스 상태 정보 업데이트
m_pClient->service.service_stat = iServiceStat;
}
// update 시간 설정
m_pClient->service.update_time = currentTime;
m_pClient->service.update_time_clock_sec = currentClock.tv_sec;
// apllication 정보 설정.
m_pClient->client_app = 0;
// 2018-02-22 NEW huibong (#31703)
// - 운영 편의성을 위해 다음 상황을 report 로깅 처리한다.
// - 1. 서비스 상태가 OK , Read only 상태에서 Disable, OUT 상황으로 변경되는 경우
// - 2. 서비스 상태가 비정상 상황(disable, out) 상황에서 정상( ok, read only) 상황으로 변경되는 경우
// 서비스 상태가 변경된 경우 - 대부분 상태 변경은 잘 일어나지 않으므로.. 빠른 로직 검사를 위해 불필요해도 넣어 둠
if( iPreviousServiceStat != iServiceStat )
{
// 현재 상태가 비정상 상황인 경우
if( iServiceStat == SERVICE_STAT_OUT || iServiceStat == SERVICE_STAT_DISABLE )
{
// 이전 상황은 정상인 경우
if( iPreviousServiceStat != SERVICE_STAT_OUT && iPreviousServiceStat != SERVICE_STAT_DISABLE )
{
// report 로깅 객체 생성
CReportLog reportLog;
std::string strErrorMessage;
strErrorMessage.clear();
if( reportLog.Init( CProcessConfig::GetInstance()->GetLogPath() ) == false )
{
reportLog.GetLastError( strErrorMessage );
LOG( LERR, "client[%s] service stat change log init failed.[%s]", strHostname.c_str(), strErrorMessage.c_str() );
}
else
{
reportLog.Write( "WAR", "[%s] [ %s, service stat changed. %s -> %s. check FHS status.]"
, PROG_NAME, strHostname.c_str()
, GetServiceStatString( iPreviousServiceStat ), GetServiceStatString( iServiceStat ) );
}
}
}
else
{
// 현재 상태가 OK, Read only 의 정상 상황인 경우
// 이전 상태가 비정상 상황인 경우
if( iPreviousServiceStat == SERVICE_STAT_OUT || iPreviousServiceStat == SERVICE_STAT_DISABLE )
{
// report 로깅 객체 생성
CReportLog reportLog;
std::string strErrorMessage;
strErrorMessage.clear();
if( reportLog.Init( CProcessConfig::GetInstance()->GetLogPath() ) == false )
{
reportLog.GetLastError( strErrorMessage );
LOG( LERR, "client[%s] service stat change log init failed.[%s]", strHostname.c_str(), strErrorMessage.c_str() );
}
else
{
reportLog.Write( "INF", "[%s] [ %s, service stat changed. %s -> %s. ]"
, PROG_NAME, strHostname.c_str()
, GetServiceStatString( iPreviousServiceStat ), GetServiceStatString( iServiceStat ) );
}
}
}
}
}
else
{
// hostname 정보 설정.
strncpy( m_pClient->hostname, strHostname.c_str(), HOST_NAME_LENGTH );
// 서비스 상태 정보 설정.
m_pClient->service.service_stat = iServiceStat;
// update 시간 설정
m_pClient->service.update_time = currentTime;
m_pClient->service.update_time_clock_sec = currentClock.tv_sec;
// apllication 정보 설정.
m_pClient->client_app = 0;
// Data 유효 여부 설정.
m_pClient->valid = 1;
}
return true;
}
// systeminfo 정보를 공유메모리에 저장 처리.
// return false : 공유메모리가 유효하지 않거나... valid 가 유효하지 않거나.. hostname 이 동일하지 않은 경우.
bool CShmClientInfo::UpdateSystemInfo( std::string & strHostname, struct SystemInfo * pData )
{
// 공유메모리가 유효하지 않은 경우..
if( m_pClient == NULL || m_pClient->use == 0 )
return false;
// 현재 시간 정보 추출.
time_t currentTime = time( 0 );
// valid 설정값이 유효하지 않은 경우..
// - service info 정보가 먼저 기록되지 않은 상태인 경우....
// - 정보를 기록하지 않는다.
if( m_pClient->valid == 0 )
{
LOG( LWAR, "client [%s] hostname[%s] info not valid. [%d].", m_pClient->client_ip, m_pClient->hostname, m_pClient->valid );
return false;
}
// hostname 정보가 동일한지 검사.
if( strcmp( m_pClient->hostname, strHostname.c_str() ) != 0 )
{
LOG( LWAR, "client [%s] hostname[%s] not match. [%s].", m_pClient->client_ip, m_pClient->hostname, strHostname.c_str() );
return false;
}
// 정보가 동일한 경우.... copy 처리
memcpy( &(m_pClient->system), pData, sizeof( struct SystemInfo ) );
m_pClient->system.update_time = currentTime;
// 2018-02-22 CHG huibong (#31703)
// - 2018-01-12 #31544, #31679 에 의해 추가된 다음 기능 제거
// -- FHS Disk hang 또는 이상 상황으로 disk busy 비정상 증가시 자동 FHS out 처리
// - 사유
// -- Disk Hang 이 아닌 상황에서도 기준값(queue 10 & busy 90%)을 초과하는 Disk Busy 상황 발생으로 잘못된 서비스 OUT 발생 가능
// - 기타
// -- Disk busy 상황시 Report 로깅 처리 기능은 유지
// -- Disk busy 판단 기준값은 그대로 유지 queue 10, busy 90%
// -- 참고로 FHS 는 queue 7, busy 50% 이상인 경우 자동으로 read only 로 변경 처리됨
// --- FHS 와 같은 기준값을 사용하지 않은 이유는 FHS 기준값은 순간적인 응답지연을 막기 위한 최소한의 기준값이기 때문.
// 현재 상태가 out, disable 상태가 아닌 정상 상황인 경우
if( m_pClient->service.service_stat != SERVICE_STAT_OUT && m_pClient->service.service_stat != SERVICE_STAT_DISABLE )
{
// system 상태가 정보가 disk busy 상황인 경우
if( pData->disk_max_queue >= REPORT_MAX_DISK_QUEUE && pData->disk_max_busy >= REPORT_MAX_DISK_BUSY )
{
// report 로깅 객체 생성
CReportLog reportLog;
std::string strErrorMessage;
strErrorMessage.clear();
if( reportLog.Init( CProcessConfig::GetInstance()->GetLogPath() ) == false )
{
reportLog.GetLastError( strErrorMessage );
LOG( LERR, "client[%s] disk busy report log init failed.[%s]", strHostname.c_str(), strErrorMessage.c_str() );
}
else
{
reportLog.Write( "WAR", "[%s] [ %s, disk busy too high. busy %4.1lf%% , queue %ju. check FHS status.]"
, PROG_NAME, strHostname.c_str(), pData->disk_max_busy, pData->disk_max_queue );
_LOG( LWAR, "client[%s] disk busy[%4.1lf %%] too high. queue[%ju]. report logging."
, strHostname.c_str(), pData->disk_max_busy, pData->disk_max_queue );
}
}
}
// NEW 2018-01-11 huibong 메모리 사용량 초과시 report 기능 추가 (#31360, #31679)
// - FHS memory 사용량이 지정된 값 이상인 경우 report 로깅 처리
// - FHS system 정보가 10 sec 마다 전송되므로.. 잦은 로깅 발생 가능
// - 따라서 분당 1회만 check 하도록 한다 (최초 요청은 기동시 초기화 이슈 있으므로.. 버리고.. 3번째 요청부터 체크)
if( m_bReportMemoryMaxUsage == true && m_iShmSystemUpdateCount % 6 == 2)
{
// FHS memory 사용률이 conf 설정값 이상인 경우.
if( pData->memory_usage >= CProcessConfig::GetInstance()->GetReportMemoryMaxUsage() )
{
CReportLog reportLog;
std::string strErrorMessage;
strErrorMessage.clear();
if( reportLog.Init( CProcessConfig::GetInstance()->GetLogPath() ) == false )
{
reportLog.GetLastError( strErrorMessage );
LOG( LERR, "client[%s] memory usage report log init failed.[%s]", strHostname.c_str(), strErrorMessage.c_str() );
}
else
{
reportLog.Write( "WAR", "[%s] [FHS (%s) memory usage too high = %d%%. total=%ju byte. free=%ju byte. check FHS status.]"
, PROG_NAME, strHostname.c_str(), pData->memory_usage, pData->memory_total, pData->memory_free );
_LOG( LWAR, "client[%s] memory usage[%d %%] too high. total[%ju] free[%ju]. report logging."
, strHostname.c_str(), pData->memory_usage, pData->memory_total, pData->memory_free );
}
}
}
// system 관련 shared memory update count 증가 처리
// - 10 sec 마다 업데이트 되므로.. 일 단위 count reset
m_iShmSystemUpdateCount++;
if( m_iShmSystemUpdateCount > 8640 )
m_iShmSystemUpdateCount = 0;
return true;
}
+83
View File
@@ -0,0 +1,83 @@
/****************************************************************************
shared memory date management class
-----------------------------------------
begin : 2015/07/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 __SHM_CLIENT_INFO_H__
#define __SHM_CLIENT_INFO_H__
#include "ShmDefine.h"
#include <string>
#include <map>
// 공유메모리 영역 중 Client 영역에 대한 접근 처리 관련 인터페이스를 제공하기 위한 클래스
class CShmClientInfo
{
private:
// 공유메모리 영역 중 client 영역에 대한 포인터
struct ClientInfo * const m_pClient;
// 서비스 상태 code 에 대한 문자열 정보를 저장하기 위한 변수.
std::map<int, std::string> m_mapServiceStatString;
// NEW 2018-01-11 huibong 메모리 사용량 초과시 report 기능 추가 (#31360, #31679)
// 기능 check 여부 판단을 위한 변수
bool m_bReportMemoryMaxUsage;
// system shared memory update count
// - 잦은 report 로깅 방지를 위한 count 처리 목적
int m_iShmSystemUpdateCount;
public:
// 생성자
CShmClientInfo( struct ClientInfo * p );
// 소멸자
~CShmClientInfo();
public:
// 공유 메모리 상태가 유효하지 여부 판단.
bool IsValid( const char * pClientAddress ) const;
// Service 상태 정보에 대한 문자열 반환.
const char * GetServiceStatString( const int & stat );
// 현재 Service 상태 정보에 대한 문자열 반환.
const char * GetServiceStatString();
// serviceinfo 정보를 공유메모리에 저장 처리.
// return false : 공유메모리가 유효하지 않은 경우...
bool UpdateServiceInfo( std::string & strHostname, int iServiceStat );
// systeminfo 정보를 공유메모리에 저장 처리.
// return false : 공유메모리가 유효하지 않거나... valid 가 유효하지 않거나.. hostname 이 동일하지 않은 경우.
bool UpdateSystemInfo( std::string & strHostname, struct SystemInfo * pData );
};
#endif /* __SHM_CLIENT_INFO_H__ */
+222
View File
@@ -0,0 +1,222 @@
/***************************************************************************
rc_mond shared memory template header
-----------------------------------------
begin : 2015/06/15
copyright : (C) 2005 Solbox Inc.
author : Storage Dev 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.
***************************************************************************/
// 본 코드는 시스템 공유메모리 생성, 접근, 해제, 삭제 처리를 수행하기 위한 코드로서
// template 코드 방식으로 구성
#ifndef __SHM_CONTROL_H__
#define __SHM_CONTROL_H__
#include <unistd.h>
#include <sys/shm.h>
#include <string.h>
/*----------- MODE Flag ---------------
SHM_R 0400 read by user
SHM_W 0200 write by user
SHM_R >> 3 0040 read by group
SHM_W >> 3 0020 write by group
SHM_R >> 6 0004 read by others
SHM_W >> 6 0002 write by others
-------------------------------------*/
////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief ShmControl 공유메모리 관련 Template Class
template <typename T>
class CShmControl
{
public:
/// @brief 생성자
CShmControl();
/// @brief 소멸자
~CShmControl();
/// @brief 공유메모리 생성, 접근 또는 기존 공유메모리 접근.
/// @param iShmKey 공유메모리 접근을 위한 키
/// @param iSize 접근하고자 하는 공유메모리의 크기
/// @param iSize 공유메모리 생성및 접근시 권한
/// @return return true or false
bool Create( key_t iShmKey, size_t iSize, int iModeFlag = SHM_R | SHM_W );
/// @brief 기존 공유메모리 접근.
/// @param iShmKey 공유메모리 접근을 위한 키
/// @param iSize 접근하고자 하는 공유메모리의 크기
/// @param iSize 공유메모리 접근시 권한
/// @return return true or false
bool Attach( key_t iShmKey, size_t iSize, int iModeFlag = SHM_R | SHM_W );
/// @brief 기존 접근한 공유메모리와의 연결을 해제
/// @return return true or false
bool Detach( void );
/// @brief 기존 접근한 공유메모리와의 연결을 해제 및 삭제
/// @return return true or false
bool Remove( void );
/// @brief 공유메모리에 대한 포인터를 반환
/// @return 생성 및 접근한 공유메모리에 대한 포인터 또는 오류 발생시 NULL
T * GetShmPtr(void) const { return m_pShm; }
/// @brief 접근한 공유메모리의 ID 값을 반환
/// @return 생성 및 접근한 공유메모리 ID Value에 대한 참조.
int GetShmId(void) const { return m_iShmId; }
private:
///< m_pShm 공유메모리에 대한 포인터
T * m_pShm;
///< m_pShmId 접근한 공유메모리의 ID Value
int m_iShmId;
};
////////////////////////////////////////////////////////////////////////////////////////////////
// 기본 멤버변수 초기화.
template < typename T >
inline CShmControl<T>::CShmControl()
: m_pShm(NULL)
, m_iShmId(-1)
{
}
template < typename T >
inline CShmControl<T>::~CShmControl()
{
// 생성된 공유메모리를 종료시 다른 프로세스들이 사용한다면.. 맘대로 삭제해서는 안되며...
// 이럴 경우 detach 처리만 해야 한다.
// Detach();
// 이와 반대로.. 다른 프로세스에서 더이상 해당 공유메모리를 사용하지 않을 경우.. remove 처리한다.
Remove();
}
// 해당 공유메모리를 생성한다. 만약 기존에 생성된 것이 있다면 그놈을 사용하도록 한다.
template < typename T >
inline bool CShmControl<T>::Create( key_t iShmKey, size_t iSize, int iModeFlag )
{
// 우선 공유 메모리를 생성해 봅니다.
m_iShmId = shmget( iShmKey, iSize, IPC_CREAT | IPC_EXCL | iModeFlag );
// 생성에 실패한 경우.
if( m_iShmId == -1)
{
// 이미 기존에 생성된 놈이 있는지 확인해 봅시다.
return Attach( iShmKey, iSize, iModeFlag);
}
// 생성에 성공한 경우.
else
{
// 생성된 공유 메모리의 포인터를 받아옵니다.
m_pShm = (T *)shmat( m_iShmId, 0, 0 );
// 받아온 공유 메모리 포인터 값이 정확하면
if( reinterpret_cast<long>(m_pShm) != -1)
{
// 원래 시스템에서 생성시 자동으로 초기화됨 => 그렇지만 새로이 생성을 하였으므로 확인 사살 초기화 ^^
memset( m_pShm, 0x00, iSize);
return true;
}
// 받아온 공유 메모리 포인터를 가져오는데 실패한 경우
// => 만든놈이 삭제해 버립시다.
else
{
Remove();
return false;
}
}
}
// 기존에 생성된 것 공유 메모리에 접근하기 위해서 사용한다..
// 키값과 크기값을 정확히 알고 있어야 ( 혹시 다른데서 사용하는 놈으로 붙을까봐 ) 접근할 수 있도록 한다.
template < typename T >
inline bool CShmControl<T>::Attach( key_t iShmKey, size_t iSize, int iModeFlag )
{
// 기존 연결은 해제 시켜 버린다. => 그냥 놓은 상태에서 다시 접속할 경우 메모리 사용량이 올라감.
Detach();
m_iShmId = shmget( iShmKey, iSize, iModeFlag);
if( m_iShmId == -1 )
return false;
else
{
// 우선 기존에 있는 것과 크기가 동일한지 검사한다.
struct shmid_ds shmInfo;
memset(&shmInfo, 0x00, sizeof(struct shmid_ds));
shmctl( m_iShmId, IPC_STAT, &shmInfo);
// shm_segsz 정의 관련 참고사항
// - FreeBSD 6.X : int type으로 정의되어.. 컴파일시 waring 발생
// - FreeBSD 10.X : size_t type
// - CentOS 6.4 : size_t type
if( shmInfo.shm_segsz != iSize)
return false;
else
{
m_pShm = (T *)shmat( m_iShmId, 0, 0 );
// reinterpret_cast 을 사용한 것은 운영체제마다 포인터 형이 다를수가 있다고 참고한 라이브러리에 나와있어서 함.
if( reinterpret_cast<long>(m_pShm) == -1)
return false;
else
return true;
}
}
}
// 접근한 공유메모리로 부터 분리시킨다. ( 접근시 설정된 멤버 변수 이용)
template < typename T >
inline bool CShmControl<T>::Detach()
{
if( m_pShm > 0 )
{
if( shmdt( m_pShm) == 0)
{
m_iShmId = -1;
m_pShm = NULL;
return true;
}
else
return false;
}
else
return true;
}
// 공유메모리를 삭제한다. 이 작업을 하기 위해서는 먼저 공유 메모리를 생성 또는 접근을 해야하며
// 이때 설정된 멤버 변수를 가지고 삭제 작업을 수행한다.
// 공유 메모리는 여러 Process 들이 사용가능하기 때문에 삭제시 신중을 기할 것.
template < typename T >
inline bool CShmControl<T>::Remove()
{
if( m_iShmId > -1)
{
if( shmctl( m_iShmId, IPC_RMID, NULL) == -1)
return false;
else
{
m_iShmId = -1;
m_pShm = NULL;
return true;
}
}
return true;
}
#endif /* __SHM_CONTROL_H__ */
+124
View File
@@ -0,0 +1,124 @@
/****************************************************************************
Shared memory define for rc_mond
-----------------------------------------
begin : 2015/06/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 __SHM_DEFINE_H__
#define __SHM_DEFINE_H__
#include <unistd.h>
#include <sys/types.h>
#include <stdint.h>
#include <time.h>
// hostname 최대 길이.
#define HOST_NAME_LENGTH 63
// client ip 주소 저장을 위한 최대 크기값
// - NULL 포함 40자까지, IPv6 지원 목적
#define IP_ADDR_LENGTH 40
// Service 상태 코드 정의
#define SERVICE_STAT_OK 0 // 정상 상태
#define SERVICE_STAT_OUT 1 // 수동 중지 상태
#define SERVICE_STAT_DISABLE 2 // 서비스 불가 상태
#define SERVICE_STAT_RONLY 3 // Read Only 상태
// ShmDefine.h 에서 정의된 서비스 상태 코드 외에 미정의된 코드 처리를 위한 정의
#define SERVICE_STAT_NOT_DEFINE -1
#ifdef __cplusplus
extern "C" {
#endif
// 각 Client 로 부터 수신된 정보를 저장하기 위한 공유메모리 정의 파일
// - 공유메모리 관련 상세 내역은 rc_mond 설계서 의 공유메모리 구조 항목을 참고할 것.
// Service 상태 정보
struct ServiceInfo {
time_t update_time; // 최종 update 시간 (local time, 시간동기화 영향 받음)
int service_stat; // Service 상태 code. ( -1: 정보 미수신 상태, 그외 csagentd 설정과 동일)
time_t update_time_clock_sec; // clock_gettime(CLOCK_MONOTONIC) 을 통해 추출된 sec 정보. expire 여부 판단을 위해 사용.
};
// System 상태 정보
struct SystemInfo {
time_t update_time; // 최종 update 시간
double load_avg[3]; // load average.
uint64_t memory_total; // Total memory size (byte 단위)
uint64_t memory_free; // Free memory size (byte 단위)
int memory_usage; // memory 사용률 (%)
uint64_t swap_total; // Total swap size (byte 단위)
uint64_t swap_used; // Used swap size (byte 단위)
int swap_usage; // swap 사용률 (%)
uint64_t storage_total; // Total Storage size (byte 단위)
uint64_t storage_avail; // Available Storage size (byte 단위)
int storage_max_usage; // Storage 중 최대 사용률 (%)
uint64_t disk_max_queue; // Disk 상태 정보 중 Max Queue Length
double disk_max_busy; // Disk 상태 정보 중 Max busy(%)
uint64_t network_packet_in; // Network inbound packet 평균 count (count/sec)
uint64_t network_packet_out; // Network outbound packet 평균 count (count/sec)
uint64_t network_traffic_in; // Network inbound 평균 traffic (Byte/sec)
uint64_t network_traffic_out; // Network outbound 평균 traffic (Byte/sec)
unsigned int tcp_total; // TCP 세션 total count
unsigned int tcp_80; // TCP 세션 중 local 80 port 를 사용하는 세션 count
unsigned int tcp_80_establish; // TCP 세션 중 local 80 port 를 사용하고.. ESTABLISH 상태인 세션 count
};
// Shared momory 상의 각 slot 구성 정보.
struct ClientInfo {
int use; // 해당 공유메모리 slot 사용 여부 (0 : 미사용, 1 : 사용 중)
int valid; // 해당 공유메모리에 저장된 data 의 유효성 여부, (0 : 유효 안함, 1 : 유효)
char client_ip[IP_ADDR_LENGTH]; // Client IP 주소, NULL 포함 40자까지, IPv6 지원 목적
int client_app; // 해당 공유메모리 slot 의 Data 가 어느 프로그램에서 수신되었는지 정의 (0 : csagentd, 1 : vmasd)
char hostname[HOST_NAME_LENGTH + 1]; // hostname, NULL 포함 최대 64자까지만 허용. 각 장비를 구분하는 Key 값으로 사용.
struct ServiceInfo service; // 수신된 서비스 상태 정보 저장
struct SystemInfo system; // 수신된 System 상태 정보 저장
};
// Shared Memory 전체 구조
struct RcInfo {
int client_max_index; // FHS Client 정보 중 유효한 slot 의 최대 Index 정보.
struct ClientInfo client[MAX_CLIENT_COUNT]; // 각 FHS Client 정보.
// 향후 RCDB, RCTS 는 수집 항목이 다르므로.. 아래에 새로 정의하여 확장 처리한다.
};
#ifdef __cplusplus
}
#endif
#endif /* __SHM_DEFINE_H__ */
+648
View File
@@ -0,0 +1,648 @@
#include "ShmManager.h"
#include "Logger.h"
#include "ProcessConfig.h"
#include <string.h>
#include <math.h>
// 생성자
CShmManager::CShmManager( struct RcInfo * p )
: m_pShm( p )
{
}
// 소멸자
CShmManager::~CShmManager()
{
}
// 멤버 변수인 공유메모리에 대한 포인터가 유효하지 여부를 판단하기 위한 함수.
bool CShmManager::IsValid() const
{
if( m_pShm != NULL )
return true;
else
return false;
}
// 공유메모리 전체 영역을 초기화하기 위한 함수.
void CShmManager::ClearAll() const
{
if( m_pShm != NULL )
{
memset( m_pShm, 0x00, sizeof( struct RcInfo ) );
// 각 Client Slot 의 서비스 상태 정보는 SERVICE_STAT_NOT_DEFINE 로 설정 처리
for( int i = 0; i < MAX_CLIENT_COUNT; i++ )
{
m_pShm->client[i].service.service_stat = SERVICE_STAT_NOT_DEFINE;
}
// client index 정보는 -1 로 초기화.
m_pShm->client_max_index = -1;
}
}
// 공유메모리 상의 Client index 을 할당
// - 전달 받은 IP 정보를 기반으로 이미 존재하는 경우.. 해당 index 반환.
// - 존재하지 않는 경우.. 적합한 index 정보를 할당하여 전달 처리.
// return 오류 발생시 -1, 그 외 client index
// 본 함수는 업무 flow 상 동시 call을 하지 않아.. lock 을 사용하지 않음.
// - 만약 동시 call 할 경우.. lock 기능 추가 필요.
int CShmManager::AllocateClientIndex( const char * pClientAddress ) const
{
// 기존에 이미 할당된 slot 이 존재하는지 확인.
int i;
int nMaxIndex = m_pShm->client_max_index;
for( i = 0 ; i <= nMaxIndex; i++ )
{
// 해당 slot 이 사용 중이고...
if( m_pShm->client[i].use == 1 )
{
// client IP 정보가 동일한 경우...
if( strcmp( pClientAddress, m_pShm->client[i].client_ip ) == 0 )
{
return i;
}
}
}
// 할당된 slot 이 존재하지 않는 경우....
// 모든 slot 을 대상으로 할당 가능한 slot 이 존재하는지 확인 후
// slot 할당 및 index 정보 업데이트 처리.
int index = -1;
for( index = 0; index < MAX_CLIENT_COUNT; index++ )
{
// 해당 slot 이 미사용 중인 경우...
if( m_pShm->client[index].use == 0 )
{
// 초기화...
memset( &( m_pShm->client[index] ), 0x00, sizeof( struct ClientInfo ) );
m_pShm->client[index].service.service_stat = SERVICE_STAT_NOT_DEFINE;
// 사용 여부 설정.
m_pShm->client[index].use = 1;
// IP 정보 복사
memcpy( m_pShm->client[index].client_ip, pClientAddress, IP_ADDR_LENGTH );
// 최대 client index 조정.
if( index > m_pShm->client_max_index )
{
m_pShm->client_max_index = index;
}
return index;
}
}
// 적합한 index 를 찾지 못한 경우...
return -1;
}
// 공유메모리 상의 Client index 조회
// - 전달 받은 IP 정보를 기반으로 이미 존재하는 경우.. 해당 index 반환.
// - 존재하지 않을 경우.. -1 반환.
// return 오류 발생시 -1, 그 외 client index
// 본 함수는 업무 flow 상 동시 call을 하지 않아.. lock 을 사용하지 않음.
// - 만약 동시 call 할 경우.. lock 기능 추가 필요.
int CShmManager::FindClientIndex( const char * pClientAddress ) const
{
// 기존에 이미 할당된 slot 이 존재하는지 확인.
int i;
int nMaxIndex = m_pShm->client_max_index;
for( i = 0; i <= nMaxIndex; i++ )
{
// 해당 slot 이 사용 중이고...
if( m_pShm->client[i].use == 1 )
{
// client IP 정보가 동일한 경우...
if( strcmp( pClientAddress, m_pShm->client[i].client_ip ) == 0 )
{
return i;
}
}
}
// 해당 index 를 찾지 못한 경우...
return -1;
}
// 공유메모리 상의 Client index 해제 처리.
void CShmManager::ReleaseClientIndex( int index ) const
{
// 잘못된 index 정보인 경우.. 그냥 무시
if( index < 0 || index >= MAX_CLIENT_COUNT )
return;
// 정상 index 인 경우...
m_pShm->client[index].use = 0;
// 최대 client index 조정.
if( m_pShm->client_max_index == index )
{
UpdateClientMaxIndex();
}
return;
}
// 공유메모리 상의 Client Max index 를 다시 조사하여 설정 처리.
void CShmManager::UpdateClientMaxIndex() const
{
for( int i = (MAX_CLIENT_COUNT - 1); i >= 0; --i )
{
// 해당 slot 이 미사용 중인 경우...
if( m_pShm->client[i].use == 1 )
{
m_pShm->client_max_index = i;
return;
}
}
// 만약 유효한 slot 이 없는 경우....
// client index 정보는 -1 로 초기화.
m_pShm->client_max_index = -1;
return;
}
// 공유메모리 상의 Client index 에 해당하는 client 영역을 반환.
struct ClientInfo * CShmManager::GetClientPtr( int index ) const
{
// 잘못된 index 정보인 경우.. 그냥 무시
if( index < 0 || index >= MAX_CLIENT_COUNT )
return NULL;
return &(m_pShm->client[index]);
}
// vmasd 에서 받은 정보를 공유메모리에 저장 처리
// - 실제 오류 상황인 경우에만.. false 반환.
// - 그외 이미 존재하는 상황 등인 경우에는 정상 처리된 것으로 반환 처리한다.
// return false
bool CShmManager::SetVmasdData( struct vmasd_broadcast_message_body * pBody ) const
{
int index = -1;
// 기존에 이미 할당된 slot 이 존재하는지 확인.
int nMaxIndex = m_pShm->client_max_index;
for( int i = 0; i <= nMaxIndex; i++ )
{
// 해당 slot 이 이미 사용 중이고...
if( m_pShm->client[i].use == 1 && m_pShm->client[i].valid == 1 )
{
// csagnetd 의 경우. client ip 를 비교했지만...
// vmasd 는 hostname 을 비교해야 함.
// - csagentd 는 공인, 사설망을 사용할 수 있어.. 사설망만을 사용하는 vmasd 와 동시 동작시 ip 가 다를 수 있다.
// hostname 이 동일한 경우...
if( strcmp( pBody->hostname, m_pShm->client[i].hostname ) == 0 )
{
// client_app 이 vmasd 인 경우...
if( m_pShm->client[i].client_app == 1 )
{
index = i;
break;
}
else
{
// csagentd 정보와 충돌인 경우... csagentd 의 정보를 우선 사용토록 처리한다.
_LOG( LINF, "vmasd shm update skip. csagentd data exist. [%s][%s]", pBody->domain, pBody->hostname );
return true;
}
}
}
}
// 기존에 할당된 slot 이 존재하지 않는 경우....
// 모든 slot 을 대상으로 할당 가능한 slot 이 존재하는지 확인 후
// slot 할당 및 index 정보 업데이트 처리.
if( index == -1 )
{
for( int j = 0; j < MAX_CLIENT_COUNT; j++ )
{
// 해당 slot 이 미사용 중인 경우...
if( m_pShm->client[j].use == 0 )
{
index = j;
// 초기화...
memset( &( m_pShm->client[j] ), 0x00, sizeof( struct ClientInfo ) );
m_pShm->client[j].service.service_stat = SERVICE_STAT_NOT_DEFINE;
// 사용 여부 설정.
m_pShm->client[j].use = 1;
// IP 정보 복사
strcpy( m_pShm->client[j].client_ip, pBody->domain );
// hostname 정보 복사.
strcpy( m_pShm->client[j].hostname, pBody->hostname );
// client_app 을 vmasd 로 설정.
m_pShm->client[j].client_app = 1;
// 최대 client index 조정.
if( j > m_pShm->client_max_index )
{
m_pShm->client_max_index = j;
}
break;
}
}
}
if( index == -1 )
{
LOG( LERR, "vmasd shm update fail. shm allocate fail.[%s][%s]", pBody->domain, pBody->hostname );
return false;
}
else
{
struct ClientInfo * pClient = &( m_pShm->client[index] );
// Body 정보를 저장 처리.
// vmasd storage 용량 정보 처리 관련...
pClient->system.storage_total = pBody->total;
pClient->system.storage_avail = pBody->free;
if( pClient->system.storage_avail == 0 )
{
if( pClient->system.storage_total == 0 )
{
// 1. total = 0, free = 0 인 경우
// - 공인 IP 정보 추출 실패, 공인 IP 를 통한 Apache 접속 테스트 실패, DNS resolve 체크 실패시...
// - 서비스 불가 (SERVICE_STAT_DISABLE) 상태로 처리.
pClient->service.service_stat = SERVICE_STAT_DISABLE;
// storage 최대 사용량 정보는 0 으로 설정 처리.
pClient->system.storage_max_usage = 0;
}
else
{
// 2. total = 정상, free = 0 인 경우
// - free 용량이 total 보다 큰 경우
// - Read Only (SERVICE_STAT_RONLY) 상태로 처리한다.
pClient->service.service_stat = SERVICE_STAT_RONLY;
// storage 최대 사용량 정보는 100 % 으로 설정 처리.
pClient->system.storage_max_usage = 100;
}
}
else
{
// free 용량 정보는 정상...
if( pClient->system.storage_total == 0 )
{
// total 정보가 0 인 경우...
// - 추출 관련 오류 발생 상황으로 판단되며...
// - Read Only (SERVICE_STAT_RONLY) 상태로 처리한다.
pClient->service.service_stat = SERVICE_STAT_RONLY;
// storage 최대 사용량 정보는 100 % 으로 설정 처리.
pClient->system.storage_max_usage = 100;
}
else
{
// total, free 정보가 모두 정상인 경우...
// 평균 사용량 정보를 추출한다.
int usage = (int)ceil( (double)( pClient->system.storage_total - pClient->system.storage_avail ) / pClient->system.storage_total * 100.0 );
// 원래는 최대 storage 사용량 의미이지만.. vmasd 는 평균 사용량으로 밖에 처리할 수 없음.
pClient->system.storage_max_usage = usage;
// pBody->manual_disable
// 2 : fsck_ufs 프로세스가 동작 중인 경우.. --> SERVICE_STAT_RONLY 로 설정.
// 1 : /user/service/vmasd.disable 파일이 존재할 경우..
// .disable 설정이 되지 않은 유효 Mount 중.. 가장 큰 Free Disk 용량(f_bfree) 이 0 인 경우... --> SERVICE_STAT_RONLY 로 설정.
// 0 : 그 외..
if( pBody->manual_disable == 0 )
{
// 정상인 상태에서...
// storeage 사용량이 conf 에 정의된 설정값 이상인 경우... 강제로 read-only 로 설정 처리한다.
if( usage >= CProcessConfig::GetInstance()->GetStorageMaxUsageToDisable() )
{
_LOG( LINF, "vmasd client[%s] hostname[%s] storage max usage over [%d/%d]. set read only stat."
, pBody->domain, pBody->hostname, usage, CProcessConfig::GetInstance()->GetStorageMaxUsageToDisable() );
pClient->service.service_stat = SERVICE_STAT_RONLY;
}
else
pClient->service.service_stat = SERVICE_STAT_OK;
}
else
{
pClient->service.service_stat = SERVICE_STAT_RONLY;
}
}
}
// vmasd 는 평균 busy 율 사용, csagentd 는 Max busy 율 사용.
// 따라서 값의 의미가 정확히 일치하지는 않지만.. 그래도 저장 처리한다.
pClient->system.disk_max_busy = pBody->disk_percent;
// vmasd 는 마지막 disk 의 queue length. csagent 는 disk 의 최대 queue length
// 따라서 값의 의미가 정확히 일치하지는 않지만.. 그래도 저장 처리한다.
pClient->system.disk_max_queue = pBody->disk_backlog;
pClient->system.tcp_80 = pBody->connection_total;
pClient->system.tcp_80_establish = pBody->connection_established;
pClient->system.network_packet_in = pBody->traffic_ip;
pClient->system.network_packet_out = pBody->traffic_op;
pClient->system.network_traffic_in = pBody->traffic_ib;
pClient->system.network_traffic_out = pBody->traffic_ob;
// 현재 시간 정보 추출 및 업데이트
time_t currentTime = time( 0 );
struct timespec currentClock;
memset( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
pClient->service.update_time = currentTime;
pClient->service.update_time_clock_sec = currentClock.tv_sec;
pClient->system.update_time = currentTime;
// Data 유효 여부 설정.
pClient->valid = 1;
return true;
}
}
// Alive (상태가 OK, Read-only) 인 FHS 목록 정보 조회 요청
// param vecResult [out] 추출된 alive fhs 목록을 저장하기 위한 vector 참조.
// return 목록 갯수
int CShmManager::GetAliveFhsList( std::vector<struct FhsServiceStatInfo> & vecResult ) const
{
int nMaxIndex = m_pShm->client_max_index;
int nResult = 0;
struct FhsServiceStatInfo stFhs;
memset( &stFhs, 0x00, sizeof( struct FhsServiceStatInfo ) );
// 결과를 저장할 vector 는 초기화 하지 않음.
// - 이미 다른 목적으로 data 를 저장하여 전달할 수 있으므로.. 초기화하지 않음.
// Max index 까지 루프를 돌면서... 검색
for( int i = 0; i <= nMaxIndex; i++ )
{
// slot 할당되어 있고.. data 가 유효한 경우...
if( m_pShm->client[i].use == 1 && m_pShm->client[i].valid == 1 )
{
// 서비스 상태가 OK, read only 인 경우...
if( m_pShm->client[i].service.service_stat == SERVICE_STAT_OK
|| m_pShm->client[i].service.service_stat == SERVICE_STAT_RONLY )
{
// update 처리가 오래된 garbage 데이터는 control 모듈에서 정리 처리하므로...
// 여기서는 체크하지 않는다.
// hostname 정보가 유효한지 확인 후...
if( strlen( m_pShm->client[i].hostname ) > 0 )
{
// hostname 정보 복사.
strncpy( stFhs.hostname, m_pShm->client[i].hostname, HOST_NAME_LENGTH );
// 서비스 상태 정보 설정.
stFhs.service_stat = m_pShm->client[i].service.service_stat;
// vector 에 결과 저장
vecResult.push_back( stFhs );
nResult += 1;
}
}
}
}
return nResult;
}
// 공유메모리에 저장된 client 정보 중 갱신되지 않는 오래된 client 정보를 정리한다.
// param expireTime [in] 공유메모리 update_time_clock_sec 정보와 expire 처리 여부를 비교하기 위한 기준값.
// return expire 처리된 client 수.
int CShmManager::ExpireClient( time_t expireTime ) const
{
int nExpireCount = 0;
// Max Index 에 오류가 있을 수도 있으므로..... 전체 slot 을 모두 검사한다.
for( int i = 0; i < MAX_CLIENT_COUNT; i++ )
{
// 해당 slot 이 사용 중이고.. data 정보가 유효한 경우...
if( m_pShm->client[i].use == 1 && m_pShm->client[i].valid == 1 && m_pShm->client[i].service.update_time_clock_sec != 0 )
{
// 최종 update clock 정보와 전달받은 expire 정보를 비교...
if( m_pShm->client[i].service.update_time_clock_sec < expireTime )
{
// 전달받은 expire 시간보다 오래된 client 인 경우....
// - 로깅 후 expire 처리한다.
struct tm timeLast;
localtime_r( &(m_pShm->client[i].service.update_time), &timeLast );
_LOG( LWAR, "client[%s] stat[%d] app[%d] expired. last update time[%04d-%02d-%02d %02d:%02d:%02d]"
, m_pShm->client[i].hostname, m_pShm->client[i].service.service_stat, m_pShm->client[i].client_app
, timeLast.tm_year + 1900, timeLast.tm_mon + 1, timeLast.tm_mday
, timeLast.tm_hour, timeLast.tm_min, timeLast.tm_sec
);
// 해제 처리 시작
// vmasd 인 경우.. valid, use 까지 reset 처리.
// csagent 인 경우.. valid 만 reset 처리 ( 이래야 세션이 살아 있는 중에 재할당을 방지하고.. 세션이 끊어지면서.. use 를 0으로 처리함)
if( m_pShm->client[i].client_app == 1 )
{
// vmasd 인 경우..
m_pShm->client[i].valid = 0;
m_pShm->client[i].use = 0;
}
else
{
// csagent 인 경우...
m_pShm->client[i].valid = 0;
}
// count 증가 처리
++nExpireCount;
}
}
}
// expire 처리된 client 가 존재하는 경우.... Max Index 재설정 처리.
if( nExpireCount > 0 )
{
UpdateClientMaxIndex();
}
return nExpireCount;
}
// GMS 로 전송할 RC 상태 정보 요청
// param vecResult [out] 추출된 목록을 저장하기 위한 vector 참조.
// return 목록 갯수
int CShmManager::GetGmsSendList( std::vector<struct PacketDataGms> & vecResult ) const
{
struct PacketDataGms stSystem;
int nResult = 0;
int nMaxIndex = m_pShm->client_max_index;
// 결과를 저장할 vector 는 초기화 하지 않음.
// - 이미 다른 목적으로 data 를 저장하여 전달할 수 있으므로.. 초기화하지 않음.
// Max index 까지 루프를 돌면서... 검색
for( int i = 0; i <= nMaxIndex; i++ )
{
// slot 할당되어 있고.. data 가 유효한 경우...
if( m_pShm->client[i].use == 1 && m_pShm->client[i].valid == 1 )
{
// 임시 객체 초기화.
memset( &stSystem, 0x00, sizeof( struct PacketDataGms ) );
// 저장할 정보 변환 처리.
strncpy( stSystem.domain, m_pShm->client[i].hostname, 511 );
stSystem.total = m_pShm->client[i].system.storage_total;
stSystem.free = m_pShm->client[i].system.storage_avail;
// 정상 상태인 경우.. system 업데이트 시간 사용..
// ronly, disable, out 등의 상황인 경우.. service 업데이트 시간 사용.
if( m_pShm->client[i].service.service_stat == SERVICE_STAT_OK )
{
stSystem.manual_disable = 0;
stSystem.stamp = m_pShm->client[i].system.update_time;
}
else
{
stSystem.stamp = m_pShm->client[i].service.update_time;
if( m_pShm->client[i].service.service_stat == SERVICE_STAT_RONLY )
{
stSystem.manual_disable = 1;
}
else if( m_pShm->client[i].service.service_stat == SERVICE_STAT_OUT)
{
stSystem.manual_disable = 3;
}
else
{
stSystem.manual_disable = 2;
}
}
// (disk_backlog/10) + (connection_established/50) 의 계산 값... ( vmasd 소스 참고)
stSystem.upload_level = ( m_pShm->client[i].system.disk_max_queue ) / 10 + (m_pShm->client[i].system.tcp_80_establish) / 50;
stSystem.disk_backlog = (int)( m_pShm->client[i].system.disk_max_queue );
stSystem.disk_percent = m_pShm->client[i].system.disk_max_busy;
stSystem.connection_established = (int)( m_pShm->client[i].system.tcp_80_establish );
stSystem.connection_total = (int)( m_pShm->client[i].system.tcp_80 );
stSystem.traffic_ip = (uint32_t)( m_pShm->client[i].system.network_packet_in );
stSystem.traffic_ib = (uint32_t)( m_pShm->client[i].system.network_traffic_in );
stSystem.traffic_op = (uint32_t)( m_pShm->client[i].system.network_packet_out );
stSystem.traffic_ob = (uint32_t)( m_pShm->client[i].system.network_traffic_out );
strncpy( stSystem.hostname, m_pShm->client[i].hostname, 63 );
// vector 에 결과 저장
vecResult.push_back( stSystem );
// result count 증가.
nResult += 1;
}
}
return nResult;
}
// Top FHS 정보 추출
// param vecResult [out] 추출된 목록을 저장하기 위한 vector 참조.
// param dDiskMaxBusyWeight [in] score 항목 계산시 disk_max_busy 정보에 대한 가중치값.
// return 목록 갯수
int CShmManager::GetWritableFhsList( std::vector<struct WritableFhsInfo> & vecResult, double dDiskMaxBusyWeight ) const
{
struct WritableFhsInfo stWritableFhs;
int nResult = 0;
int nMaxIndex = m_pShm->client_max_index;
// 결과를 저장할 vector 는 초기화 하지 않음.
// - 이미 다른 목적으로 data 를 저장하여 전달할 수 있으므로.. 초기화하지 않음.
// FHS 의 시스템 정보 업데이트가 오래된 경우..
// - GarbageClean Thread 가 지정된 시간( 20 sec ) 를 초과한 경우.. expire 처리하지만...
// - 오류가 발생할 수 있고... FHS 상태가 이상할 경우.. 해당 장비는 선택하지 않는 것이 좋으므로...
// - 업데이트가 비교적 최근에 발생한 FHS 정보만을 사용토록 한다.
// - vmasd 는 6 sec 마다... csagentd 는 4(service), 10(system) sec 마다 정보 전송...
// - 따라서.. 8 sec 초과된 정보는 사용하지 않도록 한다.
// - local time 은 시간 동기화 영향 받으므로... update_time_clock_sec 항목을 사용하여 판단한다.
time_t expireTime;
struct timespec currentClock;
memset( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
expireTime = currentClock.tv_sec - 8;
// Max index 까지 루프를 돌면서... 검색
for( int i = 0; i <= nMaxIndex; i++ )
{
// slot 할당되어 있고.. data 가 유효하고..
// write 가능 상태인 경우..
// system 상태 정보가 정상인 경우...( system 정보가 늦게 수신되므로.. 이를 반영 )
if( m_pShm->client[i].use == 1
&& m_pShm->client[i].valid == 1
&& m_pShm->client[i].service.service_stat == SERVICE_STAT_OK
&& m_pShm->client[i].system.update_time != 0 )
{
// 최종 update clock 정보와 전달받은 expire 정보를 비교...
if( m_pShm->client[i].service.update_time_clock_sec >= expireTime )
{
// expire 시간보다 오래되지 않은 최근 업데이트된 FHS 인 경우..
// 임시 객체 초기화.
memset( &stWritableFhs, 0x00, sizeof( struct WritableFhsInfo ) );
// hostname 저장.
strncpy( stWritableFhs.hostname, m_pShm->client[i].hostname, HOST_NAME_LENGTH );
// write score 계산 처리.
// - storage_max_usage 항목과
// - disk_max_busy * 전달받은 가중치 값의 합.
stWritableFhs.score = (double)( m_pShm->client[i].system.storage_max_usage )
+ ( ( m_pShm->client[i].system.disk_max_busy ) * dDiskMaxBusyWeight );
// vector 에 결과 저장
vecResult.push_back( stWritableFhs );
// result count 증가.
nResult += 1;
}
}
}
return nResult;
}
+141
View File
@@ -0,0 +1,141 @@
/****************************************************************************
shared memory date management class
-----------------------------------------
begin : 2015/06/15
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __SHM_MANAGER_H__
#define __SHM_MANAGER_H__
#include "ShmDefine.h"
#include "ProtocolVmasd.h"
#include "ProtocolGms.h"
#include <string>
#include <map>
#include <vector>
// 공유메모리에서 각 FHS 의 서비스 상태 정보 조회시 사용하기 위한 구조체
// - interface 함수에서 사용하기 위해 정의함.
struct FhsServiceStatInfo {
char hostname[HOST_NAME_LENGTH + 1]; // hostname, NULL 포함 최대 64자까지만 허용됨.
int service_stat; // Service 상태 code.
};
// 공유메모리에 저장된 각 FHS 정보 중 Write 가 가능한 FHS 정보를 추출하여 저장하기 위한 구조체
// - Top FHS 선택 모듈에 FHS 정보를 추출하여 전달하기 위해 정의함.
struct WritableFhsInfo {
char hostname[HOST_NAME_LENGTH + 1]; // hostname, NULL 포함 최대 64자까지만 허용됨.
double score; // Write 관련 시스템 모니터링 항목을 이용하여 계산된 score, 작을수록 idle
};
// 공유메모리 영역 전체에 대한 접근 처리 관련 인터페이스를 제공하기 위한 클래스
class CShmManager
{
private:
// 공유메모리에 대한 포인터
struct RcInfo * const m_pShm;
public:
// 생성자
CShmManager( struct RcInfo * p );
// 소멸자
~CShmManager();
public:
// 함수 선언시 const 구문 사용 이유
// - 멤버변수인 m_pShm 값이 변경될 경우...엉뚱한 메모리 영역을 참조하는 불상사 발생.
// - 따라서 코딩시 m_pShm 값이 변경되지 않음을 선언하여....
// - 컴파일시 문제가 없는지 확인하기 위한 목적으로 사용함.
// - map 관련 멤버 변수 사용으로 인해.. 일부 함수에서는 사용하지 못함.
// 멤버 변수인 공유메모리에 대한 포인터가 유효하지 여부를 판단하기 위한 함수.
// return 유효한 경우 true, NULL 인 경우 false
bool IsValid( void ) const;
// 공유메모리 전체 영역을 초기화하기 위한 함수.
// return
void ClearAll( void ) const;
// 공유메모리 상의 Client index 을 할당
// - 전달 받은 IP 정보를 기반으로 이미 존재하는 경우.. 해당 index 반환.
// - 존재하지 않는 경우.. 적합한 index 정보를 할당하여 전달 처리.
// return 오류 발생시 -1, 그 외 client index
// 본 함수는 업무 flow 상 동시 call을 하지 않아.. lock 을 사용하지 않음.
// - 만약 동시 call 할 경우.. lock 기능 추가 필요.
int AllocateClientIndex( const char * pClientAddress ) const;
// 공유메모리 상의 Client index 조회
// - 전달 받은 IP 정보를 기반으로 이미 존재하는 경우.. 해당 index 반환.
// - 존재하지 않을 경우.. -1 반환.
// return 오류 발생시 -1, 그 외 client index
// 본 함수는 업무 flow 상 동시 call을 하지 않아.. lock 을 사용하지 않음.
// - 만약 동시 call 할 경우.. lock 기능 추가 필요.
int FindClientIndex( const char * pClientAddress ) const;
// 공유메모리 상의 Client index 해제 처리.
void ReleaseClientIndex( int index ) const;
// 공유메모리 상의 Client Max index 를 다시 조사하여 설정 처리.
void UpdateClientMaxIndex( void ) const;
// 공유메모리 상의 Client index 에 해당하는 client 영역을 반환.
struct ClientInfo * GetClientPtr( int index ) const;
// vmasd 에서 받은 정보를 공유메모리에 저장 처리
// - 실제 오류 상황인 경우에만.. false 반환.
// - 그외 이미 존재하는 상황 등인 경우에는 정상 처리된 것으로 반환 처리한다.
// return false
bool SetVmasdData( struct vmasd_broadcast_message_body * pBody ) const;
// Alive (상태가 OK, Read-only) 인 FHS 목록 정보 조회 요청
// param vecResult [out] 추출된 alive fhs 목록을 저장하기 위한 vector 참조.
// return 목록 갯수
int GetAliveFhsList( std::vector<struct FhsServiceStatInfo> & vecResult ) const;
// 공유메모리에 저장된 client 정보 중 갱신되지 않는 오래된 client 정보를 정리한다.
// param expireTime [in] 공유메모리 update_time_clock_sec 정보와 expire 처리 여부를 비교하기 위한 기준값.
// return expire 처리된 client 수.
int ExpireClient( time_t expireTime ) const;
// GMS 로 전송할 RC 상태 정보 요청
// param vecResult [out] 추출된 목록을 저장하기 위한 vector 참조.
// return 목록 갯수
int GetGmsSendList( std::vector<struct PacketDataGms> & vecResult ) const;
// Top FHS 정보 추출
// param vecResult [out] 추출된 목록을 저장하기 위한 vector 참조.
// param dDiskMaxBusyWeight [in] score 항목 계산시 disk_max_busy 정보에 대한 가중치값.
// return 목록 갯수
int GetWritableFhsList( std::vector<struct WritableFhsInfo> & vecResult, double dDiskMaxBusyWeight ) const;
};
#endif /* __SHM_MANAGER_H__ */
+227
View File
@@ -0,0 +1,227 @@
#include "ShmMonitor.h"
#include "ShmDefine.h"
#include "ShmControl.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
// 프로세스 name
#define MONITOR_PROG_NAME "rc_show"
// 화면 갱신 time (sec)
#define DEFAULT_REFRESH_TIME 3
// Shared memory key 파일.
// 2018-02-21 CHG huibong /tmp 경로 대신 /user/service 하위에 생성되도록 경로 수정 (#31701)
// - /tmp 경로 사용시 자동 clean 처리 모듈 동작으로 삭제되는 상황 발생하여 수정 처리.
#define SHARED_MEMORY_KEY_FILE "/user/service/rc_mond.shm"
// 공유메모리 생성, 삭제 등의 Manager 객체에 대한 포인터.
CShmControl< struct RcInfo > * g_pShmCtl = NULL;
// CHG 2015-11-03 huibong
// - 일감 #24963 에 따른 기능 추가 수행.
// 사용방법 표시
void PrintUsage()
{
fprintf( stderr, "\n" );
fprintf( stderr, "Usage: " MONITOR_PROG_NAME " [OPTION] \n" );
fprintf( stderr, "Options: \n" );
fprintf( stderr, " -i {sec} Set refresh interval (sec) \n" );
fprintf( stderr, " -h Display help information \n" );
fprintf( stderr, " -v Display version \n" );
fprintf( stderr, "\n" );
fprintf( stderr, MONITOR_PROG_NAME " is Solbox Cloud Storage module.\n" );
fprintf( stderr, " - Display shared memory info that FHS system, service status collected by %s.\n", PROG_NAME );
fprintf( stderr, "\n" );
return;
}
// 버전 정보 표시
void PrintVersion()
{
fprintf( stderr, "\n" );
fprintf( stderr, MONITOR_PROG_NAME " version: " PROG_VERSION "\n\n" );
return;
}
int main( int argc, char * argv[] )
{
int iRefreshTime = DEFAULT_REFRESH_TIME;
if( argc >= 2 )
{
int opt;
while( ( opt = getopt( argc, argv, "hvi:" ) ) != -1 ) // 옵션 끝까지 파싱처리
{
switch( opt )
{
case 'h':
case '?': // 정의되지 않은 문자가 나타날 경우 getopt 에 자동반환. 도움말 표시 처리.
PrintUsage();
return EXIT_SUCCESS;
case 'v':
PrintVersion();
return EXIT_SUCCESS;
case 'i':
iRefreshTime = atoi( optarg );
break;
default: // 본 조건절은 getopt 특성상 동작하지 않지만...업무 Flow 이해(?)를 위해 유지한다.
fprintf( stderr, "Unkonwn options[%c] used. program terminated.\n", opt );
return EXIT_FAILURE;
}
}
}
// iRefreshTime 정합성 검사
// - atoi 에서 오류시 0 을 반환하므로... 무한 refresh 방지.
if( iRefreshTime <= 0 )
iRefreshTime = DEFAULT_REFRESH_TIME;
// 공유메모리 접근 처리
key_t iShmKey;
// 공유메모리 key 생성을 위한 임시 파일 생성 처리.
FILE * pShmFile = fopen( SHARED_MEMORY_KEY_FILE, "r" );
if( pShmFile != NULL )
{
// Key file 생성이 성공한 경우... 파일만 있으면 되니까.. close 처리한다.
fclose( pShmFile );
pShmFile = NULL;
}
else
{
// Key file 생성에 실패한 경우...
int errorNum = errno;
fprintf(stderr, "Shared memory key file open fail[%s] [%d][%s]\n\n", SHARED_MEMORY_KEY_FILE, errorNum, strerror( errorNum ) );
return EXIT_FAILURE;
}
// 임시 파일로 부터 key 값 추출
iShmKey = ftok( SHARED_MEMORY_KEY_FILE, 1 );
if( iShmKey == (key_t)-1 )
{
// 공유메모리 Key 생성 실패시
fprintf( stderr, "Shared memory key create failed. temp file [%s]\n\n", SHARED_MEMORY_KEY_FILE );
return EXIT_FAILURE;
}
// 공유메모리에 생성 담당 class 객체를 생성 및 공유메모리 생성 or attach
CShmControl< struct RcInfo > shmCtl;
if( shmCtl.Attach( iShmKey, sizeof( struct RcInfo ) ) == false )
{
fprintf( stderr, "Shared memory attach failed. key[%d] size[%lu].\n\n", iShmKey, sizeof( struct RcInfo ) );
return EXIT_FAILURE;
}
g_pShmCtl = &shmCtl; // 전역 포인터에 대한 포인터 지정.
// 종료 처리 관련 시그널 처리기
signal( SIGINT, ProgramExit );
struct RcInfo * pShm = shmCtl.GetShmPtr();
struct ClientInfo * pClient = NULL;
while( 1 )
{
// 중간에 rc_mond 가 종료되었는지 체크...
pShmFile = fopen( SHARED_MEMORY_KEY_FILE, "r" );
if( pShmFile == NULL )
{
// Key file open check 실패시..
fprintf( stderr, "\nshared memory key file not found [%s]. program exit.\n\n", SHARED_MEMORY_KEY_FILE );
break;
}
else
{
// key 파일 정상인 경우...
fclose( pShmFile );
pShmFile = NULL;
}
system( "clear" );
printf( "refresh time : %3d sec\n", iRefreshTime );
printf( "max client index : %3d\n", pShm->client_max_index );
printf( "svc - 0:ok, 1:manual out, 2:service disable, 3:read only\n\n");
//printf( "app - 0:csagentd, 1:vmasd\n\n" );
//printf( "shm\tvalid\tip\t\tapp\thost\t\t\tupdate\t\tsvc\tupdate\t\tload\t\tmem\tswap\tstg\tdisk\t\tnet\t\ttcp\n" );
printf( "shm\tip\t\thost\t\t\t\tupdate\t\tsvc\tupdate\t load\t\tmem\tswap\tstg\tdisk\t\ttraffic\t\t tcp\n" );
printf( "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" );
for( int i = 0; i <= pShm->client_max_index; i++ )
{
pClient = &(pShm->client[i]);
// 2018-02-22 CHG huibong #31703
// 유효하지 않은 세션이지만.. garbage clean thread 에 의해 처리되기 전까지 존재하는 FHS 항목 표시 안되록 수정.
// 운영자들이 오해할 가능성 존재해서 정상적인 FHS 만 표시되도록 수정
if( pClient->use != 0 && pClient->valid != 0)
{
//printf( "%3d\t%d\t%s\t%d\t%s\t%10ld\t%d\t%10ld\t%.2lf %.2lf %.2lf\t%3d\t%3d\t%3d\t%3ju %4.1lf %10ju %10ju\t%5u %5u %5u\n"
printf( "%3d\t%s\t%-31.30s\t%10ld\t%d\t%10ld %.2lf %.2lf %.2lf\t%3d\t%3d\t%3d\t%3ju %6.1lf %10ju %10ju %5u %5u %5u\n"
, i, pClient->client_ip
, pClient->hostname
, pClient->service.update_time, pClient->service.service_stat
, pClient->system.update_time
, pClient->system.load_avg[0], pClient->system.load_avg[1], pClient->system.load_avg[2]
, pClient->system.memory_usage
, pClient->system.swap_usage
, pClient->system.storage_max_usage
, pClient->system.disk_max_queue, pClient->system.disk_max_busy
, pClient->system.network_traffic_in, pClient->system.network_traffic_out
, pClient->system.tcp_80_establish, pClient->system.tcp_80, pClient->system.tcp_total
);
}
}
sleep( iRefreshTime );
}
g_pShmCtl = NULL;
shmCtl.Detach();
return 0;
}
void ProgramExit( int iSigno )
{
if( g_pShmCtl != NULL )
{
g_pShmCtl->Detach();
g_pShmCtl = NULL;
}
fprintf( stderr, "\nrc monitoring end by user... goog bye ^^...\n\n" );
signal( SIGINT, SIG_DFL );
exit( 0 );
}
+45
View File
@@ -0,0 +1,45 @@
/****************************************************************************
rc_mond shared momory monitoring tool
-----------------------------------------
begin : 2015/07/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 __SHM_MONITOR_H__
#define __SHM_MONITOR_H__
#include <unistd.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
// 사용 방법을 화면에 표시하기 위한 함수.
void PrintUsage( void );
// Version 정보를 화면에 표시하기 위한 함수
void PrintVersion( void );
void ProgramExit( int iSigno );
#ifdef __cplusplus
}
#endif
#endif /* __SHM_MONITOR_H__ */
+347
View File
@@ -0,0 +1,347 @@
#include "SocketCsagentd.h"
#include "Logger.h"
#include <arpa/inet.h>
#include <string.h>
// 생성자
// param [in] clientSocket client 와 통신 연결된 socket.
// param [in] szRcid RC ID 정보.
CSocketCsagentd::CSocketCsagentd( int clientSocket, const char * szRcid )
: CBaseSocket( clientSocket )
, m_iPacketHeaderLen( sizeof( m_packetHeader ) )
, m_uPacketDataLen( 0 )
{
m_strRcId = szRcid;
}
// 소멸자.
CSocketCsagentd::~CSocketCsagentd()
{
// 소멸자 Socket 명시적 Close 처리.
Close();
}
// 수신된 Data 를 전달받은 인자에 설정하여 반환 처리.
// param [out] strHostname 장비의 hostname 정보.
// param [out] pData 수신된 data
// param [in] timeout 대기 timeout (sec)
// return -1 : Socket 오류 또는 잘못된 Data 수신시 ( RC ID 가 다른 경우 포함)
// 0 : timeout 발생시
// 1 : 정상
int CSocketCsagentd::GetSystemInfo( std::string & strHostname, struct SystemInfo * pData, int timeout )
{
// Header 정보를 수신
int nResult = GetPacketHeader( timeout );
if( nResult == -1 )
{
// Socket 끊김 또는 정의되지 않은 Data 수신시.
return -1;
}
else if( nResult == 0 )
{
// timeout 발생시
return 0;
}
//_LOG( LDBG, "system info packet header OK. body size[%u]", m_uPacketDataLen );
// Header Data 정상 수신시...
// - command code 정보 확인
if( m_packetHeader.command[0] != COMMAND_TRANSFER_SYSTEM )
{
// 미정의된 command code 인 경우
LOG( LERR, "Not defined command code." );
PrintHeaderToLog();
return -1;
}
else
{
// system 상태 정보를 수신한 경우....
// - 수신된 protocol_version 정보를 확인하여 파싱 가능한지 검사.
if( m_packetHeader.protocol_version > PARSE_MAX_PROTOCOL_VERSION_SYSTEM )
{
// 파싱 불가능한 통신 규격인 경우.....
LOG( LERR, "Not support protocol version. [COMMAND_TRANSFER_SYSTEM] support[%02x] request[%02x]."
, PARSE_MAX_PROTOCOL_VERSION_SYSTEM, m_packetHeader.protocol_version );
PrintHeaderToLog();
return -1;
}
// Data 부분 수신 처리.
if( GetPacketBodySystem( strHostname, pData ) == false )
{
// body 수신 처리시 오류 발생
return -1;
}
return 1;
}
}
// 수신된 Data 를 전달받은 인자에 설정하여 반환 처리.
// param [out] strHostname 장비의 hostname 정보.
// param [out] iServiceStat 수신된 data
// param [out] iStorageMaxUsage 수신된 data
// param [in] timeout 대기 timeout (sec)
// return -1 : Socket 오류 또는 잘못된 Data 수신시 ( RC ID 가 다른 경우 포함)
// 0 : timeout 발생시
// 1 : 정상
int CSocketCsagentd::GetServiceInfo( std::string & strHostname, int & iServiceStat, int & iStorageMaxUsage, int timeout )
{
// Header 정보를 수신
int nResult = GetPacketHeader( timeout );
if( nResult == -1 )
{
// Socket 끊김 또는 정의되지 않은 Data 수신시.
return -1;
}
else if( nResult == 0 )
{
// timeout 발생시
return 0;
}
//_LOG( LDBG, "check info packet header OK. body size[%u]", m_uPacketDataLen );
// Header Data 정상 수신시...
// - command code 정보 확인
if( m_packetHeader.command[0] != COMMAND_TRANSFER_SERVICE )
{
// 미정의된 command code 인 경우
LOG( LERR, "Not defined command code." );
PrintHeaderToLog();
return -1;
}
else
{
// system 상태 정보를 수신한 경우....
// - 수신된 protocol_version 정보를 확인하여 파싱 가능한지 검사.
if( m_packetHeader.protocol_version > PARSE_MAX_PROTOCOL_VERSION_SERVICE )
{
// 파싱 불가능한 통신 규격인 경우.....
LOG( LERR, "Not support protocol version. [COMMAND_TRANSFER_SERVICE] support[%02x] request[%02x]."
, PARSE_MAX_PROTOCOL_VERSION_SERVICE, m_packetHeader.protocol_version );
PrintHeaderToLog();
return -1;
}
// Data 부분 수신 처리.
if( GetPacketBodyService( strHostname, iServiceStat, iStorageMaxUsage ) == false )
{
// body 수신 처리시 오류 발생
return -1;
}
return 1;
}
}
// Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
void CSocketCsagentd::PrintHeaderToLog()
{
_LOG( LINF, "------------------------------------" );
_LOG( LINF, "stx [%02x]", m_packetHeader.stx );
_LOG( LINF, "type [%02x]", m_packetHeader.type );
_LOG( LINF, "command [%02x][%02x][%02x][%02x]", m_packetHeader.command[0], m_packetHeader.command[1], m_packetHeader.command[2], m_packetHeader.command[3] );
_LOG( LINF, "result [%02x][%02x][%02x][%02x]", m_packetHeader.result[0], m_packetHeader.result[1], m_packetHeader.result[2], m_packetHeader.result[3] );
_LOG( LINF, "data_length [%u]", m_uPacketDataLen );
_LOG( LINF, "protocol_version [%02x]", m_packetHeader.protocol_version );
_LOG( LINF, "extend_code [%02x]", m_packetHeader.extend_code);
_LOG( LINF, "------------------------------------" );
}
// Packet Header 부분의 수신 처리를 위한 함수
// param timeout [in] 대기시간.
// retrun 0 : timeout
// 1 : 성공
// -1 : socket 오류 또는 정의된 Data 가 아닌 경우.
int CSocketCsagentd::GetPacketHeader( int timeout )
{
if( IsValidSocket() == false )
return -1;
// Socket 으로 부터 Packet Header 부분 수신.
// 수신된 정보는 멤버변수에 저장처리.
/// read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
int nRead = ReadNTimeout( &m_packetHeader, m_iPacketHeaderLen, timeout );
if( nRead == -2 )
{
// timeout 발생시
return 0;
}
else if( nRead <= 0 )
{
// timeout 을 제외한 그 밖의 오류 발생시
return -1;
}
else
{
// 정상 Data 수신시.
// STX code 검사.
if( m_packetHeader.stx != HEADER_STX_CODE_CSAGENTD )
{
LOG( LERR, "Not valid stx code." );
PrintHeaderToLog();
return -1;
}
// Type Code 검사
if( m_packetHeader.type != HEADER_TYPE_REQUEST )
{
LOG( LERR, "Not valid type code." );
PrintHeaderToLog();
return -1;
}
// Data Length 부분 값을 멤버 변수에 저장처리.
m_uPacketDataLen = ntohl( m_packetHeader.data_length );
return 1;
}
}
// COMMAND_TRANSFER_SYSTEM 요청 관련 Packet Body 부분을 수신 처리
bool CSocketCsagentd::GetPacketBodySystem( std::string & strHostname, struct SystemInfo * pData )
{
// 현재는 protocol 버전이 0x00 1개이므로.... 본 함수에 코딩...
// 향후 버전이 추가되면... 버전별 파싱 함수 따로 분리하는 것이 좋을 듯.
// protocol_version == 0x00 인 경우.
if( m_packetHeader.protocol_version == 0x00 )
{
// 수신된 Body 를 저장할 변수 생성.
struct PacketDataSystem_V00 stBody;
// Body 부분 수신 처리.
int nRead = ReadNTimeout( &stBody, m_uPacketDataLen, 15 );
if( nRead == -2 )
{
// timeout 발생시
// - 지정된 시간 동안 Header 에 기록된 Body 크기를 읽지 못했으므로. 오류로 처리
LOG( LERR, "system info body[%u] read timeout.", m_uPacketDataLen );
return false;
}
else if( nRead <= 0 )
{
// timeout 을 제외한 그 밖의 오류 발생시
LOG( LERR, "system info body read error[%d]", nRead );
return false;
}
else
{
// Body 부분 정상 수신시...
// 1. RC ID 정보 비교 후. (대소문자 무시)
// 2. 정상인 경우..수신된 Data 를 파싱하여 인자로 전달받은 data 에 저장 처리.
if( strcasecmp( m_strRcId.c_str(), stBody.rcid ) != 0 )
{
LOG( LERR, "system info body error. RC ID[%s] not match. request[%s]"
, m_strRcId.c_str(), stBody.rcid );
return false;
}
// 0x00 버전에 대한 data 파싱.
strHostname = stBody.hostname;
pData->load_avg[0] = stBody.load_avg[0];
pData->load_avg[1] = stBody.load_avg[1];
pData->load_avg[2] = stBody.load_avg[2];
pData->memory_total = stBody.memory_total;
pData->memory_free = stBody.memory_free;
pData->memory_usage = stBody.memory_usage;
pData->swap_total = stBody.swap_total;
pData->swap_used = stBody.swap_used;
pData->swap_usage = stBody.swap_usage;
pData->storage_total = stBody.storage_total;
pData->storage_avail = stBody.storage_avail;
pData->storage_max_usage = stBody.storage_max_usage;
pData->disk_max_queue = stBody.disk_max_queue;
pData->disk_max_busy = stBody.disk_max_busy;
pData->network_packet_in = stBody.network_packet_in;
pData->network_packet_out = stBody.network_packet_out;
pData->network_traffic_in = stBody.network_traffic_in;
pData->network_traffic_out = stBody.network_traffic_out;
pData->tcp_total = stBody.tcp_total;
pData->tcp_80 = stBody.tcp_80;
pData->tcp_80_establish = stBody.tcp_80_establish;
return true;
}
}
return false;
}
// COMMAND_TRANSFER_SERVICE 요청 관련 Packet Body 부분을 수신 처리
bool CSocketCsagentd::GetPacketBodyService( std::string & strHostname, int & iServiceStat, int & iStorageMaxUsage )
{
// 현재는 protocol 버전이 0x00 1개이므로.... 본 함수에 코딩...
// 향후 버전이 추가되면... 버전별 파싱 함수 따로 분리하는 것이 좋을 듯.
// protocol_version == 0x00 인 경우.
if( m_packetHeader.protocol_version == 0x00 )
{
// 수신된 Body 를 저장할 변수 생성.
struct PacketDataService_V00 stBody;
// Body 부분 수신 처리.
int nRead = ReadNTimeout( &stBody, m_uPacketDataLen, 15 );
if( nRead == -2 )
{
// timeout 발생시
// - 지정된 시간 동안 Header 에 기록된 Body 크기를 읽지 못했으므로. 오류로 처리
LOG( LERR, "service info body[%u] read timeout.", m_uPacketDataLen );
return false;
}
else if( nRead <= 0 )
{
// timeout 을 제외한 그 밖의 오류 발생시
LOG( LERR, "service info body read error[%d]", nRead );
return false;
}
else
{
// Body 부분 정상 수신시...
// 1. RC ID 정보 비교 후. (대소문자 무시)
// 2. 정상인 경우..수신된 Data 를 파싱하여 인자로 전달받은 data 에 저장 처리.
if( strcasecmp( m_strRcId.c_str(), stBody.rcid ) != 0 )
{
LOG( LERR, "service info body error. RC ID[%s] not match. request[%s]"
, m_strRcId.c_str(), stBody.rcid );
return false;
}
// 0x00 버전에 대한 data 파싱.
strHostname = stBody.hostname;
iServiceStat = stBody.service_stat;
iStorageMaxUsage = stBody.storage_max_usage;
return true;
}
}
return false;
}
+97
View File
@@ -0,0 +1,97 @@
/****************************************************************************
csagentd socket class
-----------------------------------------
begin : 2015/07/02
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 __SOCKET_CSAGENTD_H__
#define __SOCKET_CSAGENTD_H__
#include "BaseSocket.h"
#include "ShmDefine.h"
#include "ProtocolCsagentd.h"
// CSocketCsagentd
// FHS csagentd 와의 TCP socket 통신을 관리하고... Data 수신 처리를 담당하는 class
class CSocketCsagentd: public CBaseSocket
{
public:
// 생성자
// param [in] clientSocket client 와 통신 연결된 socket.
// param [in] szRcid RC ID 정보.
CSocketCsagentd( int clientSocket, const char * szRcid );
// 소멸자
~CSocketCsagentd();
// 수신된 Data 를 전달받은 인자에 설정하여 반환 처리.
// param [out] strHostname 장비의 hostname 정보.
// param [out] pData 수신된 data
// param [in] timeout 대기 timeout (sec)
// return -1 : Socket 오류 또는 잘못된 Data 수신시 ( RC ID 가 다른 경우 포함)
// 0 : timeout 발생시
// 1 : 정상
int GetSystemInfo( std::string & strHostname, struct SystemInfo * pData, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
// 수신된 Data 를 전달받은 인자에 설정하여 반환 처리.
// param [out] strHostname 장비의 hostname 정보.
// param [out] iServiceStat 수신된 data
// param [out] iStorageMaxUsage 수신된 data
// param [in] timeout 대기 timeout (sec)
// return -1 : Socket 오류 또는 잘못된 Data 수신시 ( RC ID 가 다른 경우 포함)
// 0 : timeout 발생시
// 1 : 정상
int GetServiceInfo( std::string & strHostname, int & iServiceStat, int & iStorageMaxUsage, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
private:
// Packet Header 변수
struct PacketHeaderCsagentd m_packetHeader;
// m_packetHeader 구조체의 크기를 저장하기 위한 상수
const int m_iPacketHeaderLen;
// Packet Header 에 저장된 Data 부분의 길이 정보값.
unsigned int m_uPacketDataLen;
// Packet Data 의 유효성을 검증할 RC ID 정보
// - 생성자를 통해 전달받음.
std::string m_strRcId;
protected:
// Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
void PrintHeaderToLog( void );
// Packet Header 부분의 수신 처리를 위한 함수
// param timeout [in] 대기시간.
// retrun 0 : timeout
// 1 : 성공
// -1 : socket 오류 또는 정의된 Data 가 아닌 경우.
int GetPacketHeader( int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
// COMMAND_TRANSFER_SYSTEM 요청 관련 Packet Body 부분을 수신 처리
bool GetPacketBodySystem( std::string & strHostname, struct SystemInfo * pData );
// COMMAND_TRANSFER_SERVICE 요청 관련 Packet Body 부분을 수신 처리
bool GetPacketBodyService( std::string & strHostname, int & iServiceStat, int & iStorageMaxUsage );
};
#endif /* __SOCKET_CSAGENTD_H__ */
+95
View File
@@ -0,0 +1,95 @@
#include "SocketGms.h"
#include "Logger.h"
#include "ProcessConfig.h"
#include <string.h>
#include <arpa/inet.h>
#include <stdio.h>
CSocketGms::CSocketGms()
: CBaseSocket( SOCKET_NOT_VALID )
{
// GMS 서버 정보 설정.
m_strGmsServer = CProcessConfig::GetInstance()->GetGmsServer();
m_nGmsPort = CProcessConfig::GetInstance()->GetGmsPortStat();
// 전송할 Message Header 정보 중 공통 부분 초기화.
// 전송할 Header 초기화..
memset( &m_stHeader, 0, sizeof( struct PacketHeaderGms ) );
// 전송 Header 조립..
// szTransactionID 정보는 거의 사용하지 않으므로 빈값으로 전송한다.
// ulmode 는 GMS_MSG_MODE_VRCD_STATUSD_RTS_STATUS (0x0a01) 을 Network Byte order 로 변환하여 전송.
m_stHeader.ulMode = htonl( GMS_MSG_MODE_VRCD_STATUSD_RTS_STATUS );
// szIssuer 값은 프로그램 명을 입력 처리.
sprintf(m_stHeader.szIssuer, "%s", PROG_NAME );
// szServiceID 값은 사용하지 않음.
// ulResult 값은 GMS_OK_ALL_PASSED (0x1000) 을 Network Byte Order 로 변환하여 전송.
m_stHeader.ulResult = htonl( GMS_OK_ALL_PASSED );
}
CSocketGms::~CSocketGms()
{
// 소멸자 Socket 명시적 Close 처리.
Close();
}
bool CSocketGms::SendData( unsigned int nCount, char * szBuffer, unsigned int nBufferSize )
{
// messae header 상의 ulBodyLength 는 FHS 갯수(int) 부분관 전송될 Body 부분 합 Size 임 ( Network Byte Order )
m_stHeader.ulBodyLength = htonl( sizeof(int) + nBufferSize );
//_LOG( LDBG, "GmsSendSocket: GMS send header size..[%u], count[%u], buffersize[%u] body length[%u]"
// , sizeof(struct gms_message_header), nCount, nBufferSize, sizeof(int) + nBufferSize );
// GMS 장비와 TCP 연결.
if( Connect( m_strGmsServer, m_nGmsPort ) == false )
{
LOG( LERR, "CSocketGms: GMS connect fail.[%s][%d]", m_strGmsServer.c_str(), m_nGmsPort );
Close();
return false;
}
// Message Header 부분을 전송...
if( WriteN( &m_stHeader, sizeof( struct PacketHeaderGms ) ) != sizeof( struct PacketHeaderGms ) )
{
LOG( LERR, "CSocketGms: GMS message header send fail." );
Close();
return false;
}
// Message Body 부분 중.. 장비 수 부분을 전송한다.
if( WriteN( &nCount, sizeof( int )) != sizeof(int) )
{
LOG( LERR, "CSocketGms: GMS message body count_info[%u] send fail.", nCount );
Close();
return false;
}
// Message Body 부분 전송.. (존재하는 경우만 )
if( nBufferSize > 0 )
{
if( WriteN( szBuffer, nBufferSize) != nBufferSize )
{
LOG( LERR, "CSocketGms: GMS message body[%u] send fail.", nBufferSize );
Close();
return false;
}
}
_LOG( LINF, "CSocketGms: FHS info[count: %u] send to GMS OK..GMS[%s] port[%d]", nCount, m_strGmsServer.c_str(), m_nGmsPort );
// 전송 완료 후 Connection 을 종료 처리한다. (기존 rc_vrcd 소스 확인시 전송시에만 접속 후 세션 종료 처리함 )
Close();
return true;
}
+56
View File
@@ -0,0 +1,56 @@
/****************************************************************************
Send data to GMS using tcp connection.
-----------------------------------------
begin : 2015/07/27
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 __SOCKET_GMS_H__
#define __SOCKET_GMS_H__
#include <unistd.h>
#include <stdint.h>
#include "BaseSocket.h"
#include "ProtocolGms.h"
/// @brief CSocketGms
/// 본 클래스는 GMS 와 TCP 연결을 수행한 후...
/// 전달받은 정보를 GMS 로 통신 Protocol 에 맞도록 전송 처리하는 역활을 수행한다.
class CSocketGms : public CBaseSocket
{
public:
CSocketGms();
~CSocketGms();
bool SendData( unsigned int nCount, char * szBuffer, unsigned int nBufferSize );
private:
private:
// GMS Server 정보.
std::string m_strGmsServer;
int m_nGmsPort;
// GMS 로 Data 전송시 사용할 Message Header 관련 멤버변수.
struct PacketHeaderGms m_stHeader;
};
#endif /* __SOCKET_GMS_H__ */
+365
View File
@@ -0,0 +1,365 @@
#include "SocketSendInfo.h"
#include "Logger.h"
#include "ProcessConfig.h"
#include <arpa/inet.h>
#include <string.h>
#include <iterator>
// 생성자
// param [in] clientSocket client 와 통신 연결된 socket.
CSocketSendInfo::CSocketSendInfo( int clientSocket )
: CBaseSocket( clientSocket )
, m_iPacketHeaderLen( sizeof( m_packetHeader ) )
, m_uPacketDataLen( 0 )
{
}
// 소멸자.
CSocketSendInfo::~CSocketSendInfo()
{
// 소멸자 Socket 명시적 Close 처리.
Close();
}
// Packet Header 부분의 수신 처리를 위한 함수
// param command [out] 수신된 command code
// param timeout [in] 대기시간.
// retrun 0 : timeout
// 1 : 성공
// -1 : socket 오류 또는 정의된 Data 가 아닌 경우.
int CSocketSendInfo::GetPacketHeader( char & command, int timeout )
{
if( IsValidSocket() == false )
return -1;
// Socket 으로 부터 Packet Header 부분 수신.
// 수신된 정보는 멤버변수에 저장처리.
/// read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
int nRead = ReadNTimeout( &m_packetHeader, m_iPacketHeaderLen, timeout );
if( nRead == -2 )
{
// timeout 발생시
return 0;
}
else if( nRead <= 0 )
{
// timeout 을 제외한 그 밖의 오류 발생시
return -1;
}
else
{
// 정상 Data 수신시.
// STX code 검사.
if( m_packetHeader.stx != HEADER_STX_CODE_SEND_INFO )
{
LOG( LERR, "Not valid stx code." );
PrintHeaderToLog();
return -1;
}
// Type Code 검사
if( m_packetHeader.type != HEADER_TYPE_REQUEST )
{
LOG( LERR, "Not valid type code." );
PrintHeaderToLog();
return -1;
}
// Data Length 부분 값을 멤버 변수에 저장처리.
m_uPacketDataLen = ntohl( m_packetHeader.data_length );
// command code 정보를 출력 param 에 저장 처리.
// 통신 정의상 1th 항목만 사용.
command = m_packetHeader.command[1];
return 1;
}
}
// Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
void CSocketSendInfo::PrintHeaderToLog()
{
_LOG( LINF, "------------------------------------" );
_LOG( LINF, "stx [%02x]", m_packetHeader.stx );
_LOG( LINF, "type [%02x]", m_packetHeader.type );
_LOG( LINF, "command [%02x][%02x][%02x][%02x]", m_packetHeader.command[0], m_packetHeader.command[1], m_packetHeader.command[2], m_packetHeader.command[3] );
_LOG( LINF, "result [%02x][%02x][%02x][%02x]", m_packetHeader.result[0], m_packetHeader.result[1], m_packetHeader.result[2], m_packetHeader.result[3] );
_LOG( LINF, "data_length [%u]", m_uPacketDataLen );
_LOG( LINF, "protocol_version [%02x]", m_packetHeader.protocol_version );
_LOG( LINF, "extend_code [%02x]", m_packetHeader.extend_code );
_LOG( LINF, "------------------------------------" );
}
// COMMAND_ALIVE_FHS 요청에 대한 Body 수신 처리.
// - protocol version 에 따른 처리 필요.
// param strHostname [out] 수신된 hostname 정보.
// return false socket 오류, 해당 protocol version 미지원, RC ID 불일치 등 비정상 상황.
// 그외 true
bool CSocketSendInfo::GetPacketBodyAliveFhs( std::string & strHostname )
{
// 지원 가능한 protocol version 인지 검사.
if( m_packetHeader.protocol_version > PARSE_MAX_PROTOCOL_VERSION_ALIVE_FHS )
{
// 파싱 불가능한 버전인 경우...
LOG( LERR, "Not support protocol version. [COMMAND_ALIVE_FHS] support[%02x] request[%02x]."
, PARSE_MAX_PROTOCOL_VERSION_ALIVE_FHS, m_packetHeader.protocol_version );
PrintHeaderToLog();
std::string strErrorMessage = "Not support request protocol version.";
SendResultAliveFhsError( strErrorMessage );
return false;
}
// protocol version 별 파싱 처리.
if( m_packetHeader.protocol_version == 0x00 )
{
// 0x00 버전 지원.
struct PacketDataRequestAliveFhs_V00 stBody;
memset( &stBody, 0x00, sizeof( struct PacketDataRequestAliveFhs_V00 ) );
// Body 부분 크기가.. 수신 처리할 구조체 크기와 동일한지 체크...
// - 안 할 경우.... body 크기가 저장 구조체 크기보다 큰 경우..
// - 본 모듈이 memory overflow 로 죽으므로... 방어코드 적용.
if( m_uPacketDataLen != sizeof( stBody ) )
{
LOG( LERR, "Invalid body size. [COMMAND_ALIVE_FHS] version[%02x] define[%zu] request_size[%u]"
, m_packetHeader.protocol_version, sizeof( stBody ) , m_uPacketDataLen);
PrintHeaderToLog();
std::string strErrorMessage = "Not match request(alive fhs) protocol body size.";
SendResultAliveFhsError( strErrorMessage );
return false;
}
// Body 부분 수신 처리.
int nRead = ReadNTimeout( &stBody, m_uPacketDataLen, 5 );
if( nRead == -2 )
{
// timeout 발생시
// - 지정된 시간 동안 Header 에 기록된 Body 크기를 읽지 못했으므로. 오류로 처리
LOG( LERR, "alive fhs request body[%u] read timeout.", m_uPacketDataLen );
return false;
}
else if( nRead <= 0 )
{
// timeout 을 제외한 그 밖의 오류 발생시
LOG( LERR, "alive fhs request body read error[%d]", nRead );
return false;
}
else
{
// Body 부분 정상 수신시...
// 1. RC ID 정보 비교 후. (대소문자 무시)
// 2. 정상인 경우..수신된 Data 를 파싱하여 인자로 전달받은 data 에 저장 처리.
if( strcasecmp( CProcessConfig::GetInstance()->GetRcId(), stBody.rcid ) != 0 )
{
LOG( LERR, "alive fhs body error. RC ID not match. request [%s][%s]", stBody.rcid, stBody.hostname );
std::string strErrorMessage = "RC ID not match.";
SendResultAliveFhsError( strErrorMessage );
return false;
}
// 정상인 경우...
strHostname = stBody.hostname;
return true;
}
}
else
{
// 해당 버전에 대한 파싱 코드가 아직 없는 경우...
LOG( LERR, "Not support protocol version parsing. [COMMAND_ALIVE_FHS] request[%02x].", m_packetHeader.protocol_version );
std::string strErrorMessage = "Not support request protocol version parsing.";
SendResultAliveFhsError( strErrorMessage );
return false;
}
}
// COMMAND_ALIVE_FHS 요청 관련 error message 전송
bool CSocketSendInfo::SendResultAliveFhsError( std::string & strMessage )
{
if( IsValidSocket() == false )
return false;
// 정보 전달을 위한 Packet Header 생성
struct PacketHeaderSendInfo stHeader;
memset( &stHeader, 0x00, sizeof( struct PacketHeaderSendInfo ) );
stHeader.stx = HEADER_STX_CODE_SEND_INFO;
stHeader.type = HEADER_TYPE_RESPONSE;
stHeader.command[1] = COMMAND_ALIVE_FHS;
stHeader.result[3] = RESULT_ALIVE_FHS_ERROR;
// Data body length
stHeader.data_length = htonl( strMessage.size() );
// Protocol version
// - 수신된 protocol version 과 같도록
stHeader.protocol_version = m_packetHeader.protocol_version;
// Packet Header 정보 전송
if( WriteN( &stHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
{
LOG( LERR, "[COMMAND_ALIVE_FHS] error result send fail" );
return false;
}
else
{
// message 부분이 존재하는 경우.. 전송 처리.
if( strMessage.empty() == false )
{
WriteN( strMessage.c_str(), strMessage.size() );
}
}
return true;
}
// COMMAND_ALIVE_FHS 요청 관련 SKIP result 전송
bool CSocketSendInfo::SendResultAliveFhsSkip()
{
if( IsValidSocket() == false )
return false;
// 정보 전달을 위한 Packet Header 생성
struct PacketHeaderSendInfo stHeader;
memset( &stHeader, 0x00, sizeof( struct PacketHeaderSendInfo ) );
stHeader.stx = HEADER_STX_CODE_SEND_INFO;
stHeader.type = HEADER_TYPE_RESPONSE;
stHeader.command[1] = COMMAND_ALIVE_FHS;
stHeader.result[3] = RESULT_ALIVE_FHS_SKIP;
// Data body length
stHeader.data_length = htonl( 0 );
// Protocol version
// - 수신된 protocol version 과 같도록
stHeader.protocol_version = m_packetHeader.protocol_version;
// Packet Header 정보 전송
if( WriteN( &stHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
{
LOG( LERR, "[COMMAND_ALIVE_FHS] skip result send fail" );
return false;
}
// 전송할 Body 는 없으므로.. pass.
return true;
}
// COMMAND_ALIVE_FHS 요청 관련 Alive FHS 목록 전송
bool CSocketSendInfo::SendResultAliveFhs( std::vector< struct FhsServiceStatInfo > & vecAliveFhs )
{
if( IsValidSocket() == false )
return false;
// 정보 전달을 위한 Packet Header 생성
struct PacketHeaderSendInfo stHeader;
memset( &stHeader, 0x00, sizeof( struct PacketHeaderSendInfo ) );
stHeader.stx = HEADER_STX_CODE_SEND_INFO;
stHeader.type = HEADER_TYPE_RESPONSE;
stHeader.command[1] = COMMAND_ALIVE_FHS;
stHeader.result[3] = RESULT_ALIVE_FHS_OK;
// Protocol version
// - 수신된 protocol version 과 같도록
stHeader.protocol_version = m_packetHeader.protocol_version;
// protocol vesion 에 따른 body 부분 처리
if( m_packetHeader.protocol_version == 0x00 )
{
// 전달받은 결과 목록을 result 전송 형식에 맞도록 data 조립
// - 0x00 버전에서는 data 구조가 동일하므로.. 그냥 전송 처리해도 되지만...
// - 향후 버전 확장을 대비하여... data 조립하도록 코딩 처리함.
std::vector< struct PacketDataResponseAliveFhs_V00 > vecResponse;
struct PacketDataResponseAliveFhs_V00 fhs;
std::vector< struct FhsServiceStatInfo >::iterator it;
vecResponse.clear();
// 전달받은 Alive FHS 정보를 전송하기 위한 vector 에 저장 처리한다.
// - 향후 통신 프로토콜 변경 대비 변환 처리 수행함.
for( it = vecAliveFhs.begin(); it != vecAliveFhs.end(); ++it )
{
// hostname 정보 복사.
strncpy( fhs.hostname, it->hostname, FIMNGD_HOST_NAME_MAX_LENGTH );
// 서비스 상태 정보 code
fhs.service_stat = it->service_stat;
vecResponse.push_back( fhs );
}
// Data 전송을 위한 Size 계산 및 Body 항목 조립
// 1. 목록 크기 계산.
unsigned int nListSize = sizeof( struct PacketDataResponseAliveFhs_V00 ) * vecResponse.size();
// 2. Data body length 설정.
unsigned int nTemp = 4 + nListSize; // count (int 4Byte) + PacketDataResponseAliveFhs_V00 list
stHeader.data_length = htonl( nTemp );
// 3. body 정보 중 FHS count 정보는 network byte order 변환 후 전송 ( 전송 규약)
nTemp = htonl( vecResponse.size() );
// Packet Header 정보 전송
if( WriteN( &stHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
{
LOG( LERR, "[COMMAND_ALIVE_FHS] result header send fail." );
return false;
}
else
{
// Body 부분 전송 처리.
// 1. FHS Count ( Network byte order )
WriteN( &nTemp, 4 );
// 2. Alive FHS 목록 정보.
if( vecResponse.empty() == false )
{
if( WriteN( &( vecResponse[0] ), nListSize ) != nListSize )
{
LOG( LERR, "[COMMAND_ALIVE_FHS] result body send fail. count[%zu] size[%u]", vecResponse.size(), nListSize );
return false;
}
}
}
return true;
}
else
{
// 전송 protocol 규약을 지원하지 못하는 경우....
LOG( LERR, "Not support protocol version to send resutl [COMMAND_ALIVE_FHS]. request[%02x].", m_packetHeader.protocol_version );
std::string strErrorMessage = "Not support request protocol version.";
SendResultAliveFhsError( strErrorMessage );
return false;
}
}
+88
View File
@@ -0,0 +1,88 @@
/****************************************************************************
SendInfo Worker 에서 data 송수신을 담당할 socket class
-----------------------------------------
begin : 2015/07/16
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 __SOCKET_SEND_INFO_H__
#define __SOCKET_SEND_INFO_H__
#include "BaseSocket.h"
#include "ProtocolSendInfo.h"
#include "ShmManager.h"
#include <string>
#include <vector>
// CSocketSendInfo
// Worker SendInfo 에서 TCP 통신 및 data 송수신을 담당할 class.
class CSocketSendInfo: public CBaseSocket
{
public:
// 생성자
// param [in] clientSocket client 와 통신 연결된 socket.
CSocketSendInfo( int clientSocket );
// 소멸자
~CSocketSendInfo();
private:
// Packet Header 변수
struct PacketHeaderSendInfo m_packetHeader;
// m_packetHeader 구조체의 크기를 저장하기 위한 상수
const int m_iPacketHeaderLen;
// Packet Header 에 저장된 Data 부분의 길이 정보값.
unsigned int m_uPacketDataLen;
public:
// Packet Header 부분의 수신 처리를 위한 함수
// param command [out] 수신된 command code
// param timeout [in] 대기시간.
// retrun 0 : timeout
// 1 : 성공
// -1 : socket 오류 또는 정의된 Data 가 아닌 경우.
int GetPacketHeader( char & command, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
// Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
void PrintHeaderToLog( void );
// COMMAND_ALIVE_FHS 요청에 대한 Body 수신 처리.
// - protocol version 에 따른 처리 필요.
// param strHostname [out] 수신된 hostname 정보.
// return false socket 오류, 해당 protocol version 미지원, RC ID 불일치 등 비정상 상황.
// 그외 true
bool GetPacketBodyAliveFhs( std::string & strHostname );
// COMMAND_ALIVE_FHS 요청 관련 error message 전송
bool SendResultAliveFhsError( std::string & strMessage );
// COMMAND_ALIVE_FHS 요청 관련 SKIP result 전송
bool SendResultAliveFhsSkip( void );
// COMMAND_ALIVE_FHS 요청 관련 Alive FHS 목록 전송
bool SendResultAliveFhs( std::vector< struct FhsServiceStatInfo > & vecAliveFhs );
};
#endif /* __SOCKET_SEND_INFO_H__ */
+266
View File
@@ -0,0 +1,266 @@
#include "TopFhsUpdateThread.h"
#include "Logger.h"
#include "ProcessConfig.h"
#include "ReportLog.h"
#include "Database.h"
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <algorithm> // vector sort 사용 목적
// Top FHS 정보를 RCDB 에 업데이트 하는 주기 ( sec )
// - 2015-07-29 현재 다른 모듈들은 30 sec 단위로 해당 정보를 조회하는 중.
#define TOP_FHS_UPDATE_INTERVAL 28
// RCDB 에 저장할 Top FHS 의 최대 갯수.
#define MAX_TOP_FHS_COUNT 8
// Top FHS 추출을 위한 score 계산시... disk_max_busy 항목에 부여할 가중치 정보.
// - 파트 협의에 따라.. storage_max_usage : disk_max_busy = 1 : 4 로 계산하여 처리하기로 함.
#define DISK_MAX_BUSY_WEIGHT 4.0
// 생성자..
CTopFhsUpdateThread::CTopFhsUpdateThread( CShmManager * pShmManager )
: m_pShmManager( pShmManager)
{
// Thread Handle 초기화
m_threadHandle = 0;
}
// 소멸자..
CTopFhsUpdateThread::~CTopFhsUpdateThread()
{
// Thread 가 동작 중인 경우 Thread 동작 정지 처리.
if( m_threadHandle != 0 )
pthread_cancel(m_threadHandle);
}
// Thread 를 생성하여 작업을 시작한다...
bool CTopFhsUpdateThread::Start()
{
// RCDB 연결을 위한 접속 정보 load
m_rcdbInfo.Load();
// 처리 Thread 생성
int nResult = pthread_create( &m_threadHandle, NULL, CTopFhsUpdateThread::threadFunc, this );
if( nResult != 0 )
{
// Thread 생성 실패시...
int errorNum = errno;
LOG( LERR, "TopFhsUpdateThread: Thread create failed.[%d][%s]", errorNum, strerror(errorNum) );
return false;
}
return true;
}
// Thread 함수...
void * CTopFhsUpdateThread::threadFunc( void * arg )
{
CTopFhsUpdateThread * pObject = reinterpret_cast<CTopFhsUpdateThread *>( arg );
pthread_detach( pthread_self() );
// 초기 기동시...FHS 상태 정보가 아직 수집되지 않은 상태이므로...
// 잠시 sleep 했다가.. 작업을 시작한다.
sleep( 10 );
// 주기적으로 RCDB 에 업데이트 처리 수행.
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
sleep( TOP_FHS_UPDATE_INTERVAL );
}
// Thread 종료시 Handle 초기화
pObject->m_threadHandle = 0;
return NULL;
}
bool FhsScoreSort( const struct WritableFhsInfo & fhs1, const struct WritableFhsInfo & fhs2 )
{
return ( fhs1.score < fhs2.score );
}
// 공유메모리에 저장된 정보 중 Top FHS 정보를 추출하여 RCDB 에 저장 처리한다.
void CTopFhsUpdateThread::Execute()
{
// 공유메모리로 부터 write 가능한 FHS 정보를 받아온다.
m_vecTopFhs.clear();
m_pShmManager->GetWritableFhsList( m_vecTopFhs, DISK_MAX_BUSY_WEIGHT );
// Top FHS 정보가 존재하지 않는 경우...
if( m_vecTopFhs.empty() == true )
{
_LOG( LWAR, "TopFhsUpdateThread: Top FHS is empty. rcdb update skip.." );
// 관련 내역을 report 로그에 기록하여 운영자가 확인할 수 있도록 처리.(#21020)
CReportLog reportLog;
std::string strErrorMessage;
strErrorMessage.clear();
if( reportLog.Init( CProcessConfig::GetInstance()->GetLogPath() ) == false )
{
reportLog.GetLastError( strErrorMessage );
LOG( LERR, "TopFhsUpdateThread: report log init failed.[%s]", strErrorMessage.c_str() );
}
else
{
reportLog.Write( "WAR", "[%s] [Top FHS not exist. RCDB update skip.]", PROG_NAME );
}
return;
}
// 추출된 writable FHS 가 RCDB 에 업데이트할 MAX FHS 수 보다 많은 경우...
if( m_vecTopFhs.size() > MAX_TOP_FHS_COUNT )
{
// idle 한 순으로 정렬 처리
// - score 점수가 낮은 FHS 가 idle 한 FHS 이므로...
// - score 점수가 낮은 순서대로 정렬 처리한다.
std::sort( m_vecTopFhs.begin(), m_vecTopFhs.end(), FhsScoreSort );
// debug
/*
std::vector<struct WritableFhsInfo>::iterator it;
for( it = m_vecTopFhs.begin(); it != m_vecTopFhs.end(); ++it )
{
_LOG( LDBG, "TopFhsUpdateThread: top fhs sort [%s] [%.1lf]" , it->hostname, it->score );
}
*/
// 정렬 작업 후... RCDB 에 업데이트 할 MAX FHS 수 만큼만 저장하도록 처리.
m_vecTopFhs.resize( MAX_TOP_FHS_COUNT );
}
else
{
// Top FHS 수가 RCDB 에 업데이트할 MAX FHS 수 이하인 경우...
// - 최근 RC 운영 특성상 FHS 용량은 커지고.. FHS 수는 줄고 있는 상황.
// - 이로 인해 실제 부하를 받는 FHS 까지 Top FHS 에 포함될 가능성이 높아짐.
// - 따라서 이를 최소화하기 위해 다음과 같이 처리한다.
// -- FHS 수가 MAX FHS 수의 50% 이하인 경우... 기존과 같이 그대로 업데이트 처리.
// -- FHS 수가 MAX FHS 수의 50% 를 초과하는 경우...
// --- idle 순서대로 정렬한 후
// --- 초과된 FHS 의 절반만 등록...
// ---- 5 개인 경우... MAX 50% 인 4개 기본 + 나머지 1대의 50% 인 0 개 = 최종 4대 FHS
// ---- 6 개인 경우... MAX 50% 인 4개 기본 + 나머지 2대의 50% 인 1 개 = 최종 5대 FHS
// ---- 7 개인 경우... MAX 50% 인 4개 기본 + 나머지 3대의 50% 인 1 개 = 최종 5대 FHS
// ---- 8 개인 경우... MAX 50% 인 4개 기본 + 나머지 4대의 50% 인 2 개 = 최종 6대 FHS
// FHS 수가 MAX FHS 수의 50% 를 초과하는 경우...
if( m_vecTopFhs.size() > MAX_TOP_FHS_COUNT / 2 )
{
unsigned int uSelectCount = 0;
// idle 한 순으로 정렬 처리
// - score 점수가 낮은 FHS 가 idle 한 FHS 이므로...
// - score 점수가 낮은 순서대로 정렬 처리한다.
std::sort( m_vecTopFhs.begin(), m_vecTopFhs.end(), FhsScoreSort );
// MAX 값의 절반만큼 기본 등록한 후
// 나머지 FHS 의 50% 만큼만 등록 처리.
uSelectCount = ( MAX_TOP_FHS_COUNT / 2 ) + (( m_vecTopFhs.size() - ( MAX_TOP_FHS_COUNT / 2) ) / 2 );
m_vecTopFhs.resize( uSelectCount );
}
}
// RCDB 업데이트 작업 수행.
// RCDB 처리를 수행할 객체 생성
// RCDB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
DataBase rcdb;
// RCDB 와 연결을 시도한다.
if( 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, "TopFhsUpdateThread: RCDB connection failed. Top FHS update fail." );
}
else
{
// RCDB 연결 성공시...
char szQuery[1024];
int index = 0;
std::vector<struct WritableFhsInfo>::iterator it;
// Top FHS 정보를 RCDB 에 업데이트 처리한다.
for( it = m_vecTopFhs.begin(); it != m_vecTopFhs.end(); ++it )
{
// index 정보는 1부터 시작..
++index;
// 업데이트 Query 조립.
snprintf( szQuery, 1023,
"UPDATE t_fhs_top_status SET fhs_hostname='%s', lastupdate=now() "
"WHERE fhs_top = %d"
, it->hostname, index );
//_LOG( LDBG, "TopFhsUpdateThread: Query[%s]", szQuery);
// update query 수행.
rcdb.PgDoExec( szQuery );
// 적용된 Row 수가 1 row 가 아닌 경우 인서트 처리를 해야한다.
if( rcdb.GetCmdTuples() != 1 )
{
_LOG( LDBG, "TopFhsUpdateThread: RCDB update nothing.... try insert [%d][%s]", index, it->hostname );
// 기존 Update 관련 RCDB 결과 Set 을 Clear 처리한 후
rcdb.PgClear();
// RCDB update 작업이 실패한 경우.... insert 를 시도한다.
snprintf( szQuery, 1023,
"INSERT INTO t_fhs_top_status (fhs_top, fhs_hostname, lastupdate) "
"VALUES ( %d, '%s', now())"
, index, it->hostname );
//_LOG( LDBG, "TopFhsUpdateThread: Query[%s]", szQuery);
// insert query 수행.
rcdb.PgDoExec( szQuery );
if( rcdb.PgResult( DataBase::CLEAR ) < 0 )
{
// RCDB insert query 실패시...
LOG( LERR, "TopFhsUpdateThread: RCDB insert query failed.[%s][%s]", rcdb.GetErrorMessage().c_str(), szQuery );
// Top FHS 업데이트 관련 작업을 우선 중지하고 다음 interval 후에 다시 시도한다.
break;
}
}
else
{
// Update 처리 정상 수행시... RCDB 결과 Set 을 Clear 처리한다.
rcdb.PgClear();
}
_LOG( LDBG, "TopFhsUpdateThread: RCDB update/insert OK. [%d][%s] score [%.1lf]", index, it->hostname, it->score );
} // Top FHS 의 RCDB update loop
_LOG( LINF, "TopFhsThread: RCDB Top FHS update/insert [%d] rows.", index );
// RCDB 와의 연결을 명시적으로 해제 처리한다.
rcdb.PgCloseDB();
}
return;
}
+72
View File
@@ -0,0 +1,72 @@
/****************************************************************************
Top FHS update Thread Class
-----------------------------------------
begin : 2015/07/28
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 __TOP_FHS_UPDATE_THREAD_H__
#define __TOP_FHS_UPDATE_THREAD_H__
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <vector>
#include "ShmManager.h"
#include "RcdbInfo.h"
/// @brief CTopFhsUpdateThread
/// 본 클래스는 Thread 클래스로서...
/// 주기적으로 공유메모리에 저장된 정보 중 FHS 정보를 추출하여 Top FHS 를 선정한 후
/// 해당 정보를 RCDB 에 업데이트 한다.
/// - RCDB 와 세션은 필요시 연결 후 해제 처리.
class CTopFhsUpdateThread
{
public:
// 생성 및 소멸자.
CTopFhsUpdateThread( CShmManager * pShmManager );
~CTopFhsUpdateThread();
// 작업 시작
bool Start( void );
private:
// Thread function....
static void * threadFunc( void * arg );
// 실제 작업을 담당하는 함수.
void Execute( void );
private:
// 쓰레드 핸들
pthread_t m_threadHandle;
// 공유메모리 접근을 위한 inferface 객체 (생성자를 통해 전달받음)
CShmManager * m_pShmManager;
// Top FHS 정보를 저장하기 위한 vector
// - struct WritableFhsInfo 구조체는 ShmManager.h 에 정의되어 있으며...
// - score 점수가 낮은 FHS 가 writable, idle 한 FHS 임.
std::vector<struct WritableFhsInfo> m_vecTopFhs;
// RCDB 연결 관련 접속 정보를 저장하기 위한 객체
CRcdbInfo m_rcdbInfo;
};
#endif /* __TOP_FHS_UPDATE_THREAD_H__ */
+189
View File
@@ -0,0 +1,189 @@
#include "WorkerControl.h"
#include "Logger.h"
#include "ProcessRename.h"
#include "ShmManager.h"
#include "ProcessConfig.h"
#include "GarbageCleanThread.h"
#include "GmsSendThread.h"
#include "TopFhsUpdateThread.h"
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <time.h>
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern CShmManager * g_pShmManager;
// FHS 의 vmasd 가 Broadcasting 하는 각 FHS 의 상태 정보를 수신받기 위한 UDP Socket....
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockVmasdRecv;
// FHS csagentd 가 전송하는 시스템 상태 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockSystemRecv;
// FHS csagentd 가 전송하는 서비스 상태 체크 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockCheckRecv;
// fimngd 등과 같은 외부 모듈과 통신을 통해 정보를 전달하기 위한 TCP Socket 생성.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockInfoSend;
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerControl()
{
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker Control 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerControlTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[Control:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[Control:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerControl();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::Control Process 의 signal 처리기...
void SetWorkerControlSignalHandler()
{
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 = SignalWorkerControlTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
// Worker::Control 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerControlMain()
{
// 전역 변수로 설정된 socket 중 해당 worker 에서 사용하지 않는 socket 은 close 처리한다.
if( g_sockVmasdRecv != -1 )
{
close( g_sockVmasdRecv );
g_sockVmasdRecv = -1;
}
if( g_sockSystemRecv != -1 )
{
close( g_sockSystemRecv );
g_sockSystemRecv = -1;
}
if( g_sockCheckRecv != -1 )
{
close( g_sockCheckRecv );
g_sockCheckRecv = -1;
}
if( g_sockInfoSend != -1 )
{
close( g_sockInfoSend );
g_sockInfoSend = -1;
}
// Process Rename..
set_ps_display( PROG_NAME": Control", false );
// Set Signal Handler
SetWorkerControlSignalHandler();
// 사용할 객체 초기화 처리
// 1. 공유메모리 상의 garbage 정보를 정리하기 위한 Thread
CGarbageCleanThread threadGarbageClean( g_pShmManager );
if( threadGarbageClean.Start() == false )
{
_LOG( LWAR, "[Control:%d] garbage clean thread create failed. => Process Exit", getpid() );
ReadyToExitWorkerControl();
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
}
// 2. GMS 로 RC 정보 전달 처리 Thread
CGmsSendThread threadGmsSend( g_pShmManager );
if( threadGmsSend.Start() == false )
{
_LOG( LWAR, "[Control:%d] gms send thread create failed. => Process Exit", getpid() );
ReadyToExitWorkerControl();
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
}
// 3. RCDB 상에 Top FHS 정보 갱신 처리 Thread.
CTopFhsUpdateThread threadTopFhsUpdate( g_pShmManager );
if( threadTopFhsUpdate.Start() == false )
{
_LOG( LWAR, "[Control:%d] top fhs rcdb update thread create failed. => Process Exit", getpid() );
ReadyToExitWorkerControl();
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
}
// 대기
while( 1 )
{
// 할일 없어서.. 시그널 수신까지 대기 한다.
pause();
}
// 종료시 정리 작업 수행
ReadyToExitWorkerControl();
return EXIT_SUCCESS;
}
+36
View File
@@ -0,0 +1,36 @@
/****************************************************************************
Worker::Control process main
-----------------------------------------
begin : 2015/06/03
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 __WORKER_CONTROL_H__
#define __WORKER_CONTROL_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::Control 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerControlMain( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_CONTROL_H__ */
+392
View File
@@ -0,0 +1,392 @@
#include "WorkerRecvCheck.h"
#include "Logger.h"
#include "ProcessRename.h"
#include "ShmManager.h"
#include "ProcessConfig.h"
#include "SocketCsagentd.h"
#include "ShmClientInfo.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>
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern CShmManager * g_pShmManager;
// FHS 의 vmasd 가 Broadcasting 하는 각 FHS 의 상태 정보를 수신받기 위한 UDP Socket....
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockVmasdRecv;
// FHS csagentd 가 전송하는 시스템 상태 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockSystemRecv;
// FHS csagentd 가 전송하는 서비스 상태 체크 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockCheckRecv;
// fimngd 등과 같은 외부 모듈과 통신을 통해 정보를 전달하기 위한 TCP Socket 생성.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockInfoSend;
// 각 Client 를 처리할 Thread 에 전달할 구조체
struct stRecvCheckThreadArg {
// client socket descriptor
int clientSocketDesc;
// Client 의 IP 주소 정보를 저장한 변수
// - 공유메모리 client_ip 항목 크기와 동일한 크기로 설정.
char szClientAddress[IP_ADDR_LENGTH];
// client 의 공유메모리 index 정보.
int iClientShmIndex;
};
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerRecvCheck()
{
// open 된 socket 관련 Close 처리
if( g_sockCheckRecv != -1 )
close( g_sockCheckRecv );
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker RecvCheck 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerRecvCheckTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[RecvCheck:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[RecvCheck:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerRecvCheck();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::RecvCheck Process 의 signal 처리기...
void SetWorkerRecvCheckSignalHandler()
{
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 = SignalWorkerRecvCheckTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
// Worker::RecvCheck 에서 각 Client 에 대한 처리를 수행하기 위한 Thread.
void * WorkerRecvCheckClientThread( void * arg )
{
// 전달받은 변수를 내부 변수에 저장 처리.
// client socket 관련 처리 변수 생성
CSocketCsagentd clientSocket( ( ( struct stRecvCheckThreadArg * ) arg )->clientSocketDesc, CProcessConfig::GetInstance()->GetRcId() );
// client 주소 처리 관련
char szClientAddress[IP_ADDR_LENGTH];
memcpy( szClientAddress, ( ( struct stRecvCheckThreadArg * ) arg )->szClientAddress, IP_ADDR_LENGTH );
// client shared memory index, object
int iClientShmIndex = ( ( struct stRecvCheckThreadArg * ) arg )->iClientShmIndex;
CShmClientInfo clientShm( g_pShmManager->GetClientPtr( iClientShmIndex ) );
// malloc 로 생성된 변수 해제 처리
free( arg );
// 본 thead 는 client 연결 상태에 따라 종료되므로.. process 에서 분리 처리
pthread_detach( pthread_self() );
_LOG( LINF, "[RecvCheck:%d] client thread start. [%s] shm_index [%d]", getpid(), szClientAddress, iClientShmIndex );
// socket 수신 대기
int nRead;
std::string strHostname;
int iServiceStat;
int iStorageMaxUsage;
while( 1 )
{
// 2018-02-22 CHG huibong (#31703)
// - FHS 가 Hang 상황으로 서비스 상태 정보 전송 불가한 상황인 경우....
// - GarbageCleanThread 가 있어서.. 정리 처리를 하지만.... 운영자가 이런 flow 를 잘 모르고 오해하는 경우 있음.
// - 따라서 지정된 시간 동안 Data 를 수신하지 못하는 경우... 기존 재시도 대신 연결을 종료 처리한다.
// - 대신 timeout 시간은 기존 6 sec -> 10 sec 로 조정 한다.
// Data 수신
nRead = clientSocket.GetServiceInfo( strHostname, iServiceStat, iStorageMaxUsage, 10 );
if( nRead == 0 )
{
// timeout 발생시
// - 대기가 아닌 종료 처리 수행한다.
if( clientShm.IsValid( szClientAddress ) == true )
{
g_pShmManager->ReleaseClientIndex( iClientShmIndex );
}
_LOG( LINF, "[RecvCheck:%d] client session timeout. session closed. [%s]", getpid(), szClientAddress );
break;
}
else if( nRead < 0 )
{
// Socket 오류 발생시
// - 관련 로그는 해당 모듈에서 다 찍었으므로.. 여기서는 루프 종료 처리.
// 할당된 공유메모리 해제.
// - 본 프로세스에서 할당 처리 했으므로.. 해제 처리.
if( clientShm.IsValid( szClientAddress ) == true )
g_pShmManager->ReleaseClientIndex( iClientShmIndex );
break;
}
else
{
// 정상 Data 수신시
// RC ID 검사는 socket 수신 처리시 수행했음.
// shared memory 유효성 검사
// - 유효하지 않은 경우.. 종료 처리한다.
if( clientShm.IsValid( szClientAddress ) == false )
{
LOG( LERR, "client[%s] recv check: shared memory[%d] not valid. session close."
, szClientAddress, iClientShmIndex );
break;
}
// 수신된 iStorageMaxUsage 값이... conf 에 정의된 STORAGE_MAX_USAGE_TO_DISABLE 값 이상이면.. read only 상태로 처리한다.
if( iServiceStat == SERVICE_STAT_OK )
{
if( iStorageMaxUsage >= CProcessConfig::GetInstance()->GetStorageMaxUsageToDisable() )
{
_LOG( LINF, "client[%s] hostname[%s] storage max usage over [%d/%d]. set read only stat."
, szClientAddress, strHostname.c_str(), iStorageMaxUsage, CProcessConfig::GetInstance()->GetStorageMaxUsageToDisable() );
iServiceStat = SERVICE_STAT_RONLY;
}
}
// 공유메모리에 관련 정보를 기록한다.
if( clientShm.UpdateServiceInfo( strHostname, iServiceStat ) == false )
{
// shared memory 정보가 유효하지 않은 경우..
// - 종료 처리.
LOG( LERR, "client[%s] recv [%s] chk: shm update fail. session close. stat[%s] storage_max_usage[%d] "
, szClientAddress, strHostname.c_str(), clientShm.GetServiceStatString( iServiceStat ), iStorageMaxUsage );
break;
}
else
{
_LOG( LDBG, "client[%s] recv [%s] chk: shm update. stat[%s] storage_max_usage[%d]"
, szClientAddress, strHostname.c_str(), clientShm.GetServiceStatString( iServiceStat ), iStorageMaxUsage );
}
}
}
// client 와 연결 종료 처리.
_LOG( LINF, "[RecvCheck:%d] client connection closed. [%s]", getpid(), szClientAddress );
clientSocket.Close();
// Thread 종료 관련 처리.
return NULL;
}
// Worker::RecvCheck 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerRecvCheckMain()
{
// 전역 변수로 설정된 socket 중 해당 worker 에서 사용하지 않는 socket 은 close 처리한다.
if( g_sockVmasdRecv != -1 )
{
close( g_sockVmasdRecv );
g_sockVmasdRecv = -1;
}
if( g_sockSystemRecv != -1 )
{
close( g_sockSystemRecv );
g_sockSystemRecv = -1;
}
if( g_sockInfoSend != -1 )
{
close( g_sockInfoSend );
g_sockInfoSend = -1;
}
// Process Rename..
set_ps_display( PROG_NAME": RecvCheck", false );
// Set Signal Handler
SetWorkerRecvCheckSignalHandler();
// 사용할 객체 초기화 처리
int clientSock;
struct sockaddr_in clientSockAddr;
socklen_t clientSockLen = sizeof( clientSockAddr );
pthread_t clientThreadId;
struct stRecvCheckThreadArg * pThreadArg = NULL;
while( 1 )
{
clientSock = accept( g_sockCheckRecv, ( struct sockaddr * ) &clientSockAddr, &clientSockLen );
if( clientSock == -1 )
{
// 오류 발생시.... 해당 내역 로깅 처리.
int errorNum = errno;
LOG( LERR, "[RecvCheck:%d] client accept failed [%d][%s]", getpid(), errorNum, strerror( errorNum ) );
}
else
{
// 정상적인 Client 인 경우...
// Thread 로 전달할 변수를 malloc 로 생성 처리
pThreadArg = ( struct stRecvCheckThreadArg * ) malloc( sizeof( struct stRecvCheckThreadArg ) );
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, "[RecvCheck:%d] client[%s] thread malloc failed [%d][%s]", getpid(), tempBuffer, errorNum, strerror( errorNum ) );
}
else
{
LOG( LERR, "[RecvCheck:%d] client thread malloc failed [%d][%s] and inet_ntop error[%d][%s]"
, getpid(), errorNum, strerror( errorNum ), errno, strerror( errno ) );
}
continue;
}
else
{
// 인자로 전달할 변수정보 설정.
pThreadArg->clientSocketDesc = 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 오류 발생시
// client ip 를 공유메모리 할당 관련 key 값으로 사용하므로.....
// ip 가 불분명할 경우.. 잘못된 공유메모리 할당으로 인해 문제 발생 가능.
// 따라서 관련 상세 내역 로깅 후.. 세션 종료 처리한다.
//snprintf( pThreadArg->szClientAddress, IP_ADDR_LENGTH - 1, "0.0.0.0" );
int errorNum = errno;
LOG( LERR, "[RecvCheck:%d] client ip not valid. connection closed. inet_ntop error[%d][%s]", getpid(), errorNum, strerror( errorNum ) );
close( clientSock );
free( pThreadArg );
continue;
}
// 공유메모리상에 해당 client slot 이 존재하는지 확인 및 할당 처리.
// - 만약 존재하지 않을 경우....할당 처리.
// - 조회/할당 실패시 connection 을 close 처리하고.. 다음에 다시 접속하도록 한다.
pThreadArg->iClientShmIndex = g_pShmManager->AllocateClientIndex( pThreadArg->szClientAddress );
if( pThreadArg->iClientShmIndex == -1 )
{
LOG( LERR, "[RecvCheck:%d] client[%s] shared memory allocated fail. connection close.", getpid(), pThreadArg->szClientAddress );
close( clientSock );
free( pThreadArg );
continue;
}
// Client 처리를 담당할 Thread 생성 처리
if( pthread_create( &clientThreadId, NULL, WorkerRecvCheckClientThread, (void *)pThreadArg ) != 0 )
{
// 생성 실패시...
// - 로깅
// - client socket close
// - malloc 로 생성된 전달 변수 free 처리.
int errorNum = errno;
LOG( LERR, "[RecvCheck:%d] client[%s] thread create failed [%d][%s]"
, getpid(), pThreadArg->szClientAddress, errorNum, strerror( errorNum ) );
// 할당된 공유메모리 해제.
// - 본 프로세스에서 할당 처리 했으므로.. 해제 처리.
g_pShmManager->ReleaseClientIndex( pThreadArg->iClientShmIndex );
close( clientSock );
free( pThreadArg );
}
else
{
_LOG( LDBG, "[RecvCheck:%d] client[%s] accept & client thread create ok.", getpid(), pThreadArg->szClientAddress );
}
}
}
} // while(1)
// 종료시 정리 작업 수행
ReadyToExitWorkerRecvCheck();
return EXIT_SUCCESS;
}
+36
View File
@@ -0,0 +1,36 @@
/****************************************************************************
Worker::RecvCheck process main
-----------------------------------------
begin : 2015/06/02
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 __WORKER_RECV_CHECK_H__
#define __WORKER_RECV_CHECK_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::RecvCheck 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerRecvCheckMain( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_RECV_CHECK_H__ */
+370
View File
@@ -0,0 +1,370 @@
#include "WorkerRecvSystem.h"
#include "Logger.h"
#include "ProcessRename.h"
#include "ShmManager.h"
#include "ProcessConfig.h"
#include "SocketCsagentd.h"
#include "ShmClientInfo.h"
#include "ReportLog.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>
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern CShmManager * g_pShmManager;
// FHS 의 vmasd 가 Broadcasting 하는 각 FHS 의 상태 정보를 수신받기 위한 UDP Socket....
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockVmasdRecv;
// FHS csagentd 가 전송하는 시스템 상태 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockSystemRecv;
// FHS csagentd 가 전송하는 서비스 상태 체크 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockCheckRecv;
// fimngd 등과 같은 외부 모듈과 통신을 통해 정보를 전달하기 위한 TCP Socket 생성.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockInfoSend;
// 각 Client 를 처리할 Thread 에 전달할 구조체
struct stRecvSystemThreadArg {
// client socket descriptor
int clientSocketDesc;
// Client 의 IP 주소 정보를 저장한 변수
// - 공유메모리 client_ip 항목 크기와 동일한 크기로 설정.
char szClientAddress[IP_ADDR_LENGTH];
// client 의 공유메모리 index 정보.
int iClientShmIndex;
};
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerRecvSystem()
{
// open 된 socket 관련 Close 처리
if( g_sockSystemRecv != -1 )
close( g_sockSystemRecv );
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker RecvSystem 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerRecvSystemTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[RecvSystem:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[RecvSystem:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerRecvSystem();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::RecvSystem Process 의 signal 처리기...
void SetWorkerRecvSystemSignalHandler()
{
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 = SignalWorkerRecvSystemTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
// Worker::RecvSystem 에서 각 Client 에 대한 처리를 수행하기 위한 Thread.
void * WorkerRecvSystemClientThread( void * arg )
{
// 전달받은 변수를 내부 변수에 저장 처리.
// client socket 관련 처리 변수 생성
CSocketCsagentd clientSocket( ( ( struct stRecvSystemThreadArg * ) arg )->clientSocketDesc, CProcessConfig::GetInstance()->GetRcId() );
// client 주소 처리 관련
char szClientAddress[IP_ADDR_LENGTH];
memcpy( szClientAddress, ( ( struct stRecvSystemThreadArg * ) arg )->szClientAddress, IP_ADDR_LENGTH );
// client shared memory index
int iClientShmIndex = (( struct stRecvSystemThreadArg * ) arg )->iClientShmIndex;
CShmClientInfo clientShm( g_pShmManager->GetClientPtr( iClientShmIndex ) );
// malloc 로 생성된 변수 해제 처리
free( arg );
// 본 thead 는 client 연결 상태에 따라 종료되므로.. process 에서 분리 처리
pthread_detach( pthread_self() );
_LOG( LINF, "[RecvSystem:%d] client thread start. [%s] shm_index [%d]", getpid(), szClientAddress, iClientShmIndex );
// socket 수신 대기
int nRead;
std::string strHostname;
struct SystemInfo data;
while( 1 )
{
// 2018-02-22 CHG huibong (#31703)
// - FHS 가 Hang 상황으로 system 상태 정보 전송 불가한 상황인 경우....
// - GarbageCleanThread 가 있어서.. 정리 처리를 하지만.... 운영자가 이런 flow 를 잘 모르고 오해하는 경우 있음.
// - 따라서 지정된 시간 동안 Data 를 수신하지 못하는 경우... 기존 재시도 대신 연결을 종료 처리한다.
// - 대신 timeout 시간은 기존 13 sec -> 15 sec 로 조정 한다.
// Data 수신
nRead = clientSocket.GetSystemInfo( strHostname, &data, 15 );
if( nRead == 0 )
{
// timeout 발생시
_LOG( LINF, "[RecvSystem:%d] client session timeout. session closed. [%s]", getpid(), szClientAddress );
break;
}
else if( nRead < 0 )
{
// Socket 오류 발생시
// - 관련 로그는 해당 모듈에서 다 찍었으므로.. 여기서는 루프 종료 처리.
break;
}
else
{
// 정상 Data 수신시
// RC ID 검사는 socket 수신 처리시 수행했음.
// shared memory 유효성 검사
// - 유효하지 않은 경우.. 종료 처리한다.
if( clientShm.IsValid( szClientAddress ) == false )
{
LOG( LERR, "client[%s] recv system: shared memory[%d] not valid. session closed."
, szClientAddress, iClientShmIndex );
break;
}
// shared memory 에 정보 저장 처리.
if( clientShm.UpdateSystemInfo( strHostname, &data ) == false )
{
// shared memory 정보가 유효하지 않은 경우...
// - 종료 처리.
LOG( LERR, "client[%s] recv [%s] sys: shm update fail. session close.", szClientAddress, strHostname.c_str() );
break;
}
else
{
_LOG( LDBG, "client[%s] recv [%s] sys: shm update. load5[%.2lf] mem[%d] swap[%d] storage[%d] busy[%.1lf] tcp80[%u of %u]"
, szClientAddress, strHostname.c_str()
, data.load_avg[1], data.memory_usage, data.swap_usage, data.storage_max_usage, data.disk_max_busy
, data.tcp_80_establish, data.tcp_80 );
}
}
}
// client 와 연결 종료 처리.
_LOG( LINF, "[RecvSystem:%d] client connection closed. [%s]", getpid(), szClientAddress );
clientSocket.Close();
// Thread 종료 관련 처리.
return NULL;
}
// Worker::RecvSystem 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerRecvSystemMain()
{
// 전역 변수로 설정된 socket 중 해당 worker 에서 사용하지 않는 socket 은 close 처리한다.
if( g_sockVmasdRecv != -1 )
{
close( g_sockVmasdRecv );
g_sockVmasdRecv = -1;
}
if( g_sockCheckRecv != -1 )
{
close( g_sockCheckRecv );
g_sockCheckRecv = -1;
}
if( g_sockInfoSend != -1 )
{
close( g_sockInfoSend );
g_sockInfoSend = -1;
}
// Process Rename..
set_ps_display( PROG_NAME": RecvSystem", false );
// Set Signal Handler
SetWorkerRecvSystemSignalHandler();
// 사용할 객체 초기화 처리
int clientSock;
struct sockaddr_in clientSockAddr;
socklen_t clientSockLen = sizeof( clientSockAddr );
pthread_t clientThreadId;
struct stRecvSystemThreadArg * pThreadArg = NULL;
while( 1 )
{
clientSock = accept( g_sockSystemRecv, ( struct sockaddr * ) &clientSockAddr, &clientSockLen );
if( clientSock == -1 )
{
// 오류 발생시.... 해당 내역 로깅 처리.
int errorNum = errno;
LOG( LERR, "[RecvSystem:%d] client accept failed [%d][%s]", getpid(), errorNum, strerror( errorNum ) );
}
else
{
// 정상적인 Client 인 경우...
// Thread 로 전달할 변수를 malloc 로 생성 처리
pThreadArg = ( struct stRecvSystemThreadArg * ) malloc( sizeof( struct stRecvSystemThreadArg ) );
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, "[RecvSystem:%d] client[%s] thread malloc failed [%d][%s]", getpid(), tempBuffer, errorNum, strerror( errorNum ) );
}
else
{
LOG( LERR, "[RecvSystem:%d] client thread malloc failed [%d][%s] and inet_ntop error[%d][%s]"
,getpid(), errorNum, strerror( errorNum ), errno, strerror( errno ) );
}
continue;
}
else
{
// 인자로 전달할 변수정보 설정.
pThreadArg->clientSocketDesc = 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 오류 발생시
// client ip 를 공유메모리 할당 관련 key 값으로 사용하므로.....
// ip 가 불분명할 경우.. 잘못된 공유메모리 할당으로 인해 문제 발생 가능.
// 따라서 관련 상세 내역 로깅 후.. 세션 종료 처리한다.
//snprintf( pThreadArg->szClientAddress, IP_ADDR_LENGTH-1, "0.0.0.0" );
int errorNum = errno;
LOG( LERR, "[RecvSystem:%d] client ip not valid. connection closed. inet_ntop error[%d][%s]", getpid(), errorNum, strerror( errorNum ) );
close( clientSock );
free( pThreadArg );
continue;
}
// 공유메모리상에 해당 client slot 이 존재하는지 확인한다.
// - 만약 존재하지 않을 경우....
// - service check 관련 정보가 아직 수신되지 않은 상태이므로..... connection 을 close 처리하고.. 다음에 다시 접속하도록 한다.
pThreadArg->iClientShmIndex = g_pShmManager->FindClientIndex( pThreadArg->szClientAddress );
if( pThreadArg->iClientShmIndex == -1 )
{
LOG( LERR, "[RecvSystem:%d] client[%s] shared memory not allocated. connection close.", getpid(), pThreadArg->szClientAddress );
close( clientSock );
free( pThreadArg );
continue;
}
// Client 처리를 담당할 Thread 생성 처리
if( pthread_create( &clientThreadId, NULL, WorkerRecvSystemClientThread, (void *)pThreadArg ) != 0 )
{
// 생성 실패시...
// - 로깅
// - client socket close
// - malloc 로 생성된 전달 변수 free 처리.
int errorNum = errno;
LOG( LERR, "[RecvSystem:%d] client[%s] thread create failed [%d][%s]"
, getpid(), pThreadArg->szClientAddress, errorNum, strerror( errorNum ) );
// 할당된 공유메모리 해제
// - WorkerRecvCheck 에서 할당했음로.. 그 쪽에서 해제 권한 가짐.
close( clientSock );
free( pThreadArg );
}
else
{
_LOG( LDBG, "[RecvSystem:%d] client[%s] accept & client thread create ok.", getpid(), pThreadArg->szClientAddress );
}
}
}
} // while(1)
// 종료시 정리 작업 수행
ReadyToExitWorkerRecvSystem();
return EXIT_SUCCESS;
}
+36
View File
@@ -0,0 +1,36 @@
/****************************************************************************
Worker::RecvSystem process main
-----------------------------------------
begin : 2015/06/02
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 __WORKER_RECV_SYSTEM_H__
#define __WORKER_RECV_SYSTEM_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::RecvSystem 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerRecvSystemMain( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_RECV_SYSTEM_H__ */
+416
View File
@@ -0,0 +1,416 @@
#include "WorkerRecvVmasd.h"
#include "Logger.h"
#include "ProcessRename.h"
#include "ShmManager.h"
#include "FixedQueue.h"
#include "ProtocolVmasd.h"
#include "ProcessConfig.h"
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
// vmasd broadcast message 를 수신하기 위한 buffer size
// - vmasd 는 Header 352 Byte, Body 640 Byte => 총 992 Byte 전달함 (BroadcastMessage.h 참고)
// - 따라서 향후 확장으로 고려하여 기존 rc_vrcd 9216 Byte -> 1024 Byte 로 변경함.
#define MAX_SOCKET_BUFFER_SIZE 1024
// vmasd 로 부터 전달받은 Data 를 파싱 thread 에 전달시 사용할 Queue 의 최대 갯수
#define MAX_DATA_QUEUE_COUNT 3000
// 수신 처리를 담당할 Thread 의 수
#define MAX_THREAD_RECEIVER 10
// vmasd 수신 시작 delay time (sec)
#define RECV_START_DELAY_SEC 5
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern CShmManager * g_pShmManager;
// FHS 의 vmasd 가 Broadcasting 하는 각 FHS 의 상태 정보를 수신받기 위한 UDP Socket....
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockVmasdRecv;
// FHS csagentd 가 전송하는 시스템 상태 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockSystemRecv;
// FHS csagentd 가 전송하는 서비스 상태 체크 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockCheckRecv;
// fimngd 등과 같은 외부 모듈과 통신을 통해 정보를 전달하기 위한 TCP Socket 생성.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockInfoSend;
// Data 수신부와 이를 파싱하여 처리할 Thread 객체간에 Data 전달을 위한 Queue 객체 생성
CFixedQueue * g_pStatDataQueue = NULL;
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerRecvVmasd()
{
// open 된 socket 관련 close 처리
if( g_sockVmasdRecv != -1 )
close( g_sockVmasdRecv );
// Queue 객체 해제
if( g_pStatDataQueue != NULL )
{
CFixedQueue * temp = g_pStatDataQueue;
g_pStatDataQueue = NULL;
delete temp;
}
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker RecvVmasd 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerRecvVmasdTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[RecvVmasd:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[RecvVmasd:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerRecvVmasd();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::RecvVmasd Process 의 signal 처리기...
void SetWorkerRecvVmasdSignalHandler()
{
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 = SignalWorkerRecvVmasdTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
// 전역 Queue 에 저장된 vmasd 수신 data 를 파싱하여 공유 메모리에 저장 처리를 담당하는 Thread
void * WorkerRecvVmasdSaveThread( void * arg )
{
// 메인 함수에서 이 스레드가 종료되기를 기다리지 않게 분리
pthread_detach( pthread_self() );
_LOG( LINF, "[RecvVmasd:%d] SaveThread: start...", getpid() );
// Queue pop data 저장 관련 변수 초기화.
int pop_result;
char szTempBuffer[MAX_SOCKET_BUFFER_SIZE];
//struct vmasd_broadcast_message_header * pHeader = (struct vmasd_broadcast_message_header *)szTempBuffer;
struct vmasd_broadcast_message_body * pBody = ( struct vmasd_broadcast_message_body * )( szTempBuffer + sizeof( struct vmasd_broadcast_message_header ) ); // sizeof 값은 352 Byte (64Bit OS )
// 본 Thread 는 main 에서 vmasd 정보를 수신한 후 동작해야 하므로... 5 sec 정도 기다린 후 동작시킨다.
sleep( RECV_START_DELAY_SEC );
// 루프를 돌면서... Queue 에 저장된 vmasd 수신 정보를 POP -> 파싱하여.. 저장 처리한다.
// g_pStatDataQueue 값은 프로세스 종료 처리시 NULL 로 변경된다.. 따라서 본 Thread 가 종료될 수 있도록 처리한다.
while( g_pStatDataQueue != NULL )
{
// Queue 에서 Data 를 Pop 한다..
// Queue 객체 자체에서 인자값을 전달한 버퍼를 memset 하므로.. 따로 수행하지 않는다.
pop_result = g_pStatDataQueue->Pop( szTempBuffer, MAX_SOCKET_BUFFER_SIZE );
if( pop_result == 0 ) // Pop 성공시..
{
/*
_LOG( LDBG, "[RecvVmasd:%d] SaveThread: header [%s][0x%04x][%s][%s][%s][%d][%d]", getpid()
, pHeader->szTransactionID, ntohl( pHeader->ulMode ), pHeader->szIssuer, pHeader->szServiceID, pHeader->szRCID
, ntohl( pHeader->ulResult ), ntohl( pHeader->ulBodyLength ) );
_LOG( LDBG, "[RecvVmasd:%d] SaveThread: body [%s] [%lu][%lu] [%d][%d] [%d][%d] [%d][%d] [%u][%u][%u][%u] [%s] [%lu]" , getpid()
, pBody->domain
, pBody->total, pBody->free
, pBody->manual_disable, pBody->upload_level
, pBody->disk_backlog, pBody->disk_percent
, pBody->connection_established, pBody->connection_total
, pBody->traffic_ip, pBody->traffic_ib, pBody->traffic_op, pBody->traffic_ob, pBody->hostname
, pBody->large_block );
*/
// RC ID 검사는 앞의 수신 처리부에서 했으므로..... pass
// body 부분 중 domain 항목은 client ip 정보로.. 수신 모듈에서 대체처리 했음.
// 수신된 정보를 공유메모리에 저장 처리한다.
if( g_pShmManager->SetVmasdData( pBody ) == false )
{
LOG( LERR, "[RecvVmasd:%d] SaveThread: shm update fail.[%s][%s]", getpid(), pBody->domain, pBody->hostname );
}
}
else if( pop_result == 1 ) // Queue 가 empty 인 경우..
{
// pop 할 data 가 없으므로.. 잠시 쉰다.. ( 0.5 sec 정도 )
//_LOG( LDBG, "RecvVmasd[%d]:SaveThread: queue empty. sleep", getpid());
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
}
else // 나머지 상황은 오류..
{
LOG( LERR, "[RecvVmasd:%d] SaveThread: queue pop unknown error.[%d]", getpid(), pop_result );
}
}
// Thread 종료 관련 처리.
return NULL;
}
// Worker::RecvVmasd 에서 vmasd 정보를 수신처리하기 위한 Thread.
void * WorkerRecvVmasdClientThread( void * arg )
{
// 메인 함수에서 이 스레드가 종료되기를 기다리지 않게 분리
pthread_detach( pthread_self() );
_LOG( LINF, "[RecvVmasd:%d] ReceiveThread: start...", getpid() );
// vmasd broadcast data 수신을 위한 준비
struct sockaddr_in clientSocketAddr;
socklen_t clientSocketAddrLen = sizeof( clientSocketAddr );
char szClientAddress[INET_ADDRSTRLEN];
memset( szClientAddress, 0x00, sizeof( szClientAddress ) );
char szSocketBuffer[MAX_SOCKET_BUFFER_SIZE];
int nRead;
int nResult;
struct vmasd_broadcast_message_header * pHeader = NULL;
struct vmasd_broadcast_message_body * pBody = NULL;
// vmasd broadcast 정보 수신 대기
while( 1 )
{
// vmasd 로부터 수신된 Data read 대기
nRead = recvfrom( g_sockVmasdRecv, &szSocketBuffer, MAX_SOCKET_BUFFER_SIZE, 0, ( struct sockaddr * )&clientSocketAddr, &clientSocketAddrLen );
// 수신 오류 발생시
if( nRead < 0 )
{
LOG( LWAR, "RecvVmasd socket recvfrom error[%d][%s]", errno, strerror( errno ) );
continue;
}
if( inet_ntop( AF_INET, &( clientSocketAddr.sin_addr ), szClientAddress, sizeof( szClientAddress ) ) == NULL )
{
LOG( LERR, "RecvVmasd inet_ntop error[%d][%s]", errno, strerror( errno ) );
}
// 로그 기록 목적으로 로깅처리
_LOG( LDBG, "VmasdReceiver[%d][%lu]: vmasd[%s] received. length[%d]", getpid(), pthread_self(), szClientAddress, nRead );
// 1. vmasd message 의 경우 broadcasting message 이므로.. 사설 네트워크 구간이 분리되지 않은 경우... 다른 RC 의 메시지가 수신될 확률이 높다.
// 따라서. 동일 RC 에서 전달된 메시지인지 먼저 확인한다.
pHeader = ( struct vmasd_broadcast_message_header * )szSocketBuffer;
if( strcasecmp( pHeader->szRCID, CProcessConfig::GetInstance()->GetRcId() ) != 0 )
{
// 다른 RC 로 부터 vmasd 메시지를 수신한 경우... 수신 Data 는 그냥 버린다.
LOG( LWAR, "RecvVmasd: Not match RCID[%s][%s]", pHeader->szRCID, szClientAddress );
continue;
}
// Debug 목적으로 Data 파싱 처리 후 출력...
/*
_LOG( LDBG, "BroadcastReceiver[%d]: vmasd [%s][0x%04x][%s][%s][%s][%d][%d]", getpid()
, pHeader->szTransactionID, ntohl(pHeader->ulMode), pHeader->szIssuer, pHeader->szServiceID, pHeader->szRCID
, ntohl(pHeader->ulResult), ntohl(pHeader->ulBodyLength) );
*/
// 2. 기존 rc_vrcd 소스 코드를 참조하여...
// 수신된 메시시 값 중 ntohl(stHeader->ulMode) == MSG_MODE_VMA_APACHE_DISKSIZE ( 0x0100 ) 인 경우에만 처리..
// 그외 메시지에 대해서는 Invalid Protocol 로 처리한다.
if( ntohl( pHeader->ulMode ) != MSG_MODE_VMA_APACHE_DISKSIZE ) // #define MSG_MODE_VMA_APACHE_DISKSIZE 0x0100
{
LOG( LWAR, "RecvVmasd: Invalid protocol received.[0x%04x][%s]", ntohl( pHeader->ulMode ), szClientAddress );
}
else if( nRead != ( sizeof( struct vmasd_broadcast_message_header ) + sizeof( struct vmasd_broadcast_message_body ) ) )
{
// 수신된 packet 크기는 64Bit OS 에서 992 Byte 이어야 함
// - sizeof( struct vmasd_broadcast_message_header ) => 352 (64Bit OS)
// - sizeof( struct vmasd_broadcast_message_body ) => 640 (64Bit OS)
LOG( LWAR, "RecvVmasd: Invalid packet length [%s]. receive[%d]. normal[%lu] "
, szClientAddress, nRead
, ( sizeof( struct vmasd_broadcast_message_header ) + sizeof( struct vmasd_broadcast_message_body ) ) );
}
else
{
// Data 를 Queue 에 저장하기 전에.. IP 정보를 전달해야 하지만... 저장할 공간이 없으므로...
// 수신된 body 중 domain, hostname 항목이 중복되지만... domain 항목은 사용하지 않는 항목이므로....
// domain 항목에 수신된 IP 정보를 기록처리한다.
pBody = ( struct vmasd_broadcast_message_body * )( szSocketBuffer + sizeof( struct vmasd_broadcast_message_header ) ); // sizeof 값은 352 Byte (64Bit OS )
memcpy( pBody->domain, szClientAddress, sizeof( szClientAddress ) );
// Data 를 Queue 에 저장.
if( g_pStatDataQueue != NULL )
{
nResult = g_pStatDataQueue->Push( szSocketBuffer, nRead );
// Push 처리시 반환값
// -1 : 전달 인자값에 NULL 존재
// -2 : Queue 생성시 설정한 Data 크기보다 더 큰 nRead 값을 전달한 경우.
// 1 : Queue 생성시 설정한 최대 Queue 갯수를 초과한 경우..
// 0 : 정상 처리
if( nResult != 0 )
{
LOG( LWAR, "RecvVmasd: queue push error[%d]", nResult );
}
}
}
} // while(1)
// Thread 종료 관련 처리.
return NULL;
}
// Worker::RecvVmasd 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerRecvVmasdMain()
{
// 전역 변수로 설정된 socket 중 해당 worker 에서 사용하지 않는 socket 은 close 처리한다.
if( g_sockSystemRecv != -1 )
{
close( g_sockSystemRecv );
g_sockSystemRecv = -1;
}
if( g_sockCheckRecv != -1 )
{
close( g_sockCheckRecv );
g_sockCheckRecv = -1;
}
if( g_sockInfoSend != -1 )
{
close( g_sockInfoSend );
g_sockInfoSend = -1;
}
// Process Rename..
set_ps_display( PROG_NAME": RecvVmasd", false );
// Set Signal Handler
SetWorkerRecvVmasdSignalHandler();
// 사용할 객체 초기화 처리
// Data 전달을 담당할 Queue 객체 생성
g_pStatDataQueue = new CFixedQueue( MAX_SOCKET_BUFFER_SIZE, MAX_DATA_QUEUE_COUNT );
if( g_pStatDataQueue == NULL )
{
// 객체 생성 실패시
LOG( LERR, "[RecvVmasd:%d] data queue create failed. process exit", getpid() );
ReadyToExitWorkerRecvVmasd();
return EXIT_FAILURE;
}
// Queue 로 전달된 Data 를 파싱하여 공유메모리에 저장 처리를 담당할 Thread 를 생성한다.
pthread_t workerSaveThreadId;
if( pthread_create( &workerSaveThreadId, NULL, WorkerRecvVmasdSaveThread, NULL ) != 0 )
{
int errorNum = errno;
LOG( LERR, "[RecvVmasd:%d] save thread create failed[%d][%s]. process exit", getpid(), errorNum, strerror( errorNum ) );
ReadyToExitWorkerRecvVmasd();
return EXIT_FAILURE;
}
// csagentd 수신 처리 모듈이 먼저 기동되어야.. 공유메모리 동시 write 문제를 회피할 수 있다.
sleep( RECV_START_DELAY_SEC );
// vmasd 로 부터 UDP broadcasting 정보를 수신 처리할 Thread 생성 처리.
pthread_t workerReceiveThreadId;
for( int i = 0; i < MAX_THREAD_RECEIVER; i++ )
{
if( pthread_create( &workerReceiveThreadId, NULL, WorkerRecvVmasdClientThread, NULL ) != 0 )
{
int errorNum = errno;
LOG( LERR, "[RecvVmasd:%d] receive thread create failed[%d][%s]. process exit", getpid(), errorNum, strerror( errorNum ) );
ReadyToExitWorkerRecvVmasd();
return EXIT_FAILURE;
}
}
sleep( 1 );
_LOG( LINF, "[RecvVmasd:%d] receive thread count[%d] create success.", getpid(), MAX_THREAD_RECEIVER );
// 대기
while( 1 )
{
// 할일 없어서.. 시그널 수신까지 대기 한다.
pause();
}
// 종료시 정리 작업 수행
ReadyToExitWorkerRecvVmasd();
return EXIT_SUCCESS;
}
+36
View File
@@ -0,0 +1,36 @@
/****************************************************************************
Worker::RecvVmasd process main
-----------------------------------------
begin : 2015/06/02
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 __WORKER_RECV_VMASD_H__
#define __WORKER_RECV_VMASD_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::RecvVmasd 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerRecvVmasdMain( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_RECV_VMASD_H__ */
+389
View File
@@ -0,0 +1,389 @@
#include "WorkerSendInfo.h"
#include "Logger.h"
#include "ProcessRename.h"
#include "ShmManager.h"
#include "ProcessConfig.h"
#include "AliveFhsCacheThread.h"
#include "SocketSendInfo.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>
// 요청 수신 관련 최대 대기 시간. (sec)
#define MAX_REQUEST_TIMEOUT 60
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern CShmManager * g_pShmManager;
// FHS 의 vmasd 가 Broadcasting 하는 각 FHS 의 상태 정보를 수신받기 위한 UDP Socket....
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockVmasdRecv;
// FHS csagentd 가 전송하는 시스템 상태 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockSystemRecv;
// FHS csagentd 가 전송하는 서비스 상태 체크 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockCheckRecv;
// fimngd 등과 같은 외부 모듈과 통신을 통해 정보를 전달하기 위한 TCP Socket 생성.
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockInfoSend;
// Alive FHS Cache Thread 객체 대한 포인터.
CAliveFhsCacheThread * g_pThreadAliveFhs = NULL;
// 각 Client 를 처리할 Thread 에 전달할 구조체
struct stSendInfoThreadArg {
// client socket descriptor
int clientSocketDesc;
// Client 의 IP 주소 정보를 저장한 변수
// - 공유메모리 client_ip 항목 크기와 동일한 크기로 설정.
char szClientAddress[IP_ADDR_LENGTH];
};
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerSendInfo()
{
// open 된 socket 관련 Close 처리
if( g_sockInfoSend != -1 )
close( g_sockInfoSend );
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker SendInfo 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerSendInfoTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[SendInfo:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[SendInfo:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerSendInfo();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::SendInfo Process 의 signal 처리기...
void SetWorkerSendInfoSignalHandler()
{
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 = SignalWorkerSendInfoTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
// Worker::SendInfo 에서 각 Client 에 대한 처리를 수행하기 위한 Thread.
void * WorkerSendInfoClientThread( void * arg )
{
// 전달받은 변수를 내부 변수에 저장 처리.
// client socket 관련 처리 변수 생성
CSocketSendInfo clientSocket( ( ( struct stSendInfoThreadArg * ) arg )->clientSocketDesc );
// client 주소 처리 관련
char szClientAddress[IP_ADDR_LENGTH];
memcpy( szClientAddress, ( ( struct stSendInfoThreadArg * ) arg )->szClientAddress, IP_ADDR_LENGTH );
// malloc 로 생성된 변수 해제 처리
free( arg );
// 본 thead 는 client 연결 상태에 따라 종료되므로.. process 에서 분리 처리
pthread_detach( pthread_self() );
_LOG( LINF, "[SendInfo:%d] client thread start. [%s]", getpid(), szClientAddress );
// 송수신 처리 관련 변수
int nResult;
char command;
// hostname 수신 정보를 저장하기 위한 변수.
std::string strHostname;
// Alive FHS 목록정보 저장 처리를 위한 vector
std::vector<struct FhsServiceStatInfo> vecAliveFhs;
// socket 수신 대기
while( 1 )
{
// 요청 수신 대기
nResult = clientSocket.GetPacketHeader( command, MAX_REQUEST_TIMEOUT );
if( nResult == 0 )
{
// timeout 발생시...
// - server 모드로 동작하므로... 지정된 시간동안 요청이 없는 경우 세션을 종료 처리한다.
_LOG( LINF, "[SendInfo:%d] client[%s] request timeout[%d]. session close.", getpid(), szClientAddress, MAX_REQUEST_TIMEOUT );
break;
}
else if( nResult < 0 )
{
// Socket 종료, 오류 또는 정의되지 않은 형식의 패킷 header 수신시...
// 오류 내역은 socket 모듈에서 로깅하므로... 그냥 종료 처리.
break;
}
else
{
// 정상 header 수신시.
// - 수신된 command 에 따른 처리.
if( command == COMMAND_ALIVE_FHS )
{
// Alive FHS 목록 요청인 경우..
// Body 부분에 대한 처리.
// - 각 protocol version 별 처리.
if( clientSocket.GetPacketBodyAliveFhs( strHostname ) == false )
{
// Data 수신 실패, 지원하지 않는 protocol version 또는 RC ID 정보가 다른 경우....
// - 세션 종료 처리.
// - 오류 내역은 위의 해당 함수에서 client 로 전송 처리함.
LOG( LERR, "[SendInfo:%d] client[%s] alive fhs request body fail. session close.", getpid(), szClientAddress );
break;
}
// Alive FHS 목록 조회.
vecAliveFhs.clear();
if( g_pThreadAliveFhs->GetAliveFhs( vecAliveFhs ) == -1 )
{
// 공유메모리의 FHS 정보가 아직 불완전하여 SKIP 처리해야 하는 경우.
if( clientSocket.SendResultAliveFhsSkip() == false )
{
// 전송 실패 발생시.
LOG( LERR, "[SendInfo:%d] client[%s][%s] alive fhs skip send error. session close."
, getpid(), szClientAddress, strHostname.c_str() );
break;
}
}
else
{
// Alive FHS 목록 전송 처리.
if( clientSocket.SendResultAliveFhs( vecAliveFhs ) == false )
{
// 전송 실패 발생시.
LOG( LERR, "[SendInfo:%d] client[%s][%s] alive fhs send error. session close."
, getpid(), szClientAddress, strHostname.c_str() );
break;
}
else
{
// 전송 성공시
_LOG( LDBG, "[SendInfo:%d] client[%s][%s] alive fhs send ok. fhs count[%zu]"
, getpid(), szClientAddress, strHostname.c_str(), vecAliveFhs.size() );
}
}
}
else
{
// 정의되지 않은 command 인 경우..
// - 오류 로깅 후 세션 종료 처리.
LOG( LERR, "[SendInfo:%d] client[%s] request type not defined. session close.", getpid(), szClientAddress );
clientSocket.PrintHeaderToLog();
break;
}
}
}
// client 와 연결 종료 처리.
clientSocket.Close();
_LOG( LINF, "[SendInfo:%d] client connection closed. [%s]", getpid(), szClientAddress );
// Thread 종료 관련 처리.
return NULL;
}
// Worker::SendInfo 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerSendInfoMain()
{
// 전역 변수로 설정된 socket 중 해당 worker 에서 사용하지 않는 socket 은 close 처리한다.
if( g_sockVmasdRecv != -1 )
{
close( g_sockVmasdRecv );
g_sockVmasdRecv = -1;
}
if( g_sockSystemRecv != -1 )
{
close( g_sockSystemRecv );
g_sockSystemRecv = -1;
}
if( g_sockCheckRecv != -1 )
{
close( g_sockCheckRecv );
g_sockCheckRecv = -1;
}
// Process Rename..
set_ps_display( PROG_NAME": SendInfo", false );
// Set Signal Handler
SetWorkerSendInfoSignalHandler();
// 사용할 객체 초기화 처리
// Alive FHS 목록을 주기적으로 추출할 Thread 모듈 초기화 및 기동 처리.
CAliveFhsCacheThread threadAliveFhsCache( g_pShmManager );
if( threadAliveFhsCache.Start() == false )
{
_LOG( LWAR, "[SendInfo:%d] alive fhs cache thread create failed. => Process Exit", getpid() );
ReadyToExitWorkerSendInfo();
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
}
// 각 Client thread 에서 해당 객체 접근이 가능토록 전역 포인터에 저장 처리.
g_pThreadAliveFhs = &threadAliveFhsCache;
// 접속 Client 에 대한 처리.
int clientSock;
struct sockaddr_in clientSockAddr;
socklen_t clientSockLen = sizeof( clientSockAddr );
pthread_t clientThreadId;
struct stSendInfoThreadArg * pThreadArg = NULL;
while( 1 )
{
clientSock = accept( g_sockInfoSend, ( struct sockaddr * ) &clientSockAddr, &clientSockLen );
if( clientSock == -1 )
{
// 오류 발생시.... 해당 내역 로깅 처리.
int errorNum = errno;
LOG( LERR, "[SendInfo:%d] client accept failed [%d][%s]", getpid(), errorNum, strerror( errorNum ) );
}
else
{
// 정상적인 Client 인 경우...
// 처리 Thread 로 전달할 변수를 malloc 로 생성 처리
pThreadArg = ( struct stSendInfoThreadArg * ) malloc( sizeof( struct stSendInfoThreadArg ) );
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, "[SendInfo:%d] client[%s] thread malloc failed [%d][%s]", getpid(), tempBuffer, errorNum, strerror( errorNum ) );
}
else
{
LOG( LERR, "[SendInfo:%d] client thread malloc failed [%d][%s] and inet_ntop error[%d][%s]"
, getpid(), errorNum, strerror( errorNum ), errno, strerror( errno ) );
}
continue;
}
else
{
// 인자로 전달할 변수정보 설정.
pThreadArg->clientSocketDesc = 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 정보는 그냥 로깅 목적등으로 전달되는 정보이므로.. 0.0.0.0 으로 설정 처리한다.
int errorNum = errno;
LOG( LERR, "[SendInfo:%d] client ip not valid. inet_ntop error[%d][%s]", getpid(), errorNum, strerror( errorNum ) );
snprintf( pThreadArg->szClientAddress, IP_ADDR_LENGTH - 1, "0.0.0.0" );
}
// Client 처리를 담당할 Thread 생성 처리
if( pthread_create( &clientThreadId, NULL, WorkerSendInfoClientThread, (void *)pThreadArg ) != 0 )
{
// 생성 실패시...
// - 로깅
// - client socket close
// - malloc 로 생성된 전달 변수 free 처리.
int errorNum = errno;
LOG( LERR, "[SendInfo:%d] client[%s] thread create failed [%d][%s]"
, getpid(), pThreadArg->szClientAddress, errorNum, strerror( errorNum ) );
close( clientSock );
free( pThreadArg );
}
else
{
_LOG( LDBG, "[SendInfo:%d] client[%s] accept & client thread create ok.", getpid(), pThreadArg->szClientAddress );
}
}
}
} // while(1)
// 종료시 정리 작업 수행
ReadyToExitWorkerSendInfo();
return EXIT_SUCCESS;
}
+36
View File
@@ -0,0 +1,36 @@
/****************************************************************************
Worker::SendInfo process main
-----------------------------------------
begin : 2015/06/03
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 __WORKER_SEND_INFO_H__
#define __WORKER_SEND_INFO_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::SendInfo 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerSendInfoMain( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_SEND_INFO_H__ */