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
+176
View File
@@ -0,0 +1,176 @@
Revision 1539
-------------------
수정일 : 2021-05-18
수정자 : 유희곤
- NEW: Telegraf Hang 감지 및 자동 재기동 처리기능 추가 (#33542)
- FreeBSD 용 telegraf 잦은 hang 상황 발생 관련
- telegraf 상태를 1분 단위로 모니터링 하여.....
- 미기동, hang 상황을 탐지하고... 자동 재기동 등의 처리 기능을 추가
- 또한 기동된지 2일 이상된 telegraf process 를 자동 재기동 처리.
- 이를 위해 telegraf control 용 worker 프로세스 추가
Revision 1511
-------------------
수정일 : 2020-10-30
수정자 : 유희곤
- NEW: FreeBSD 12.x 지원 TCP 세션 정보 수집 관련 OS 버전별 코드 분리 (#33233)
- FreeBSD 12.x 에서 include/netinet/tcp_var.h 에서 tcpcb 구조체 변경이 발생
- 이로 인해 기존 FreeBSD 6.x, 10.x 에서 동작하는 TCP 세션 정보 추출 코드가 컴파일 되지 않는 문제 발생
- FreeBSD 12.x 의 /usr/src/usr.bin/netstat/inet.c 소스를 참고로..
- __FreeBSD_version 코드를 추가하여 FreeBSD 6.x, 10.x 와 FreeBSD 12.x 에서 분리된 코드로 컴파일 되도록 수정 처리함.
- 수정된 파일 : SystemInfo.cpp
- CHG: FreeBSD 12.x 의 gcc 9.3 사용시 warning 발생 코드 수정 (#33233)
- gcc 9.3 에서 strncpy 관련 warning 발생
- strncpy 특성상 n 값 크기만큼 copy 처리되므로... 크기 계산시 sizeof-1 처리 필요 ( sizeof(dest)-1 )
- 수정된 파일 : ServiceCheck.cpp
Revision 1493
-------------------
수정일 : 2020-04-08
수정자 : 유희곤
- CHG: IP 정보 추출시 hang 발생 회피를 위해 기동시 최초 1회만 IP 정보 추출 (#32967)
- NIC 할당된 IP 정보 추출을 위해 getifaddrs() 호출시 종종 지연 응답으로 인해 serive out 상황 발생
- 이를 회피하기 위해 기동시 최초 1회만 IP 정보를 추출하도록 수정
- 수정된 파일 : ServiceCheck.cpp
- CHG: fsck process check 기능을 사용하지 않도록 수정 (#32973)
- fsck 프로세스 동작 여부 check를 위해 popen() - fgets() 함수 사용하는 경우
- 장비 부하 상황에서 fgets() 호출 처리가 지연으로 인해 service out 상황 발생
- 이에 fsck check 는 ZFS 사용 등으로 인해 더 이상 유효하지 않으므로 check 기능 자체를 비활성화 처리함
- popen() - fgets() 사용 관련 dig, zpool 등의 호출은 지연 없는 것으로 테스트 됨.
- 수정된 파일 : ServiceCheck.cpp
- CHG: 컴파일 최적화 옵션 O3 -> O2 로 변경
- 일부 FreeBSD 10.x 장비에서 core 파일 생성 이슈가 있어.. 기본 최적화 옵션으로 변경.
Revision 1466
-------------------
수정일 : 2019-09-06
수정자 : 유희곤
- CHG: 로깅 처리 보완 (#32550)
- 기존에는 실패 처리만 로깅하여 문제 발생 FHS 에서 hang 등의 상태 발견시
- 어느 부분에서 hang 상태인지 파악이 불가
- 이를 개선하기 위해 service, system, storage check 관련 각 action 마다 로깅 처리 추가
- 일부 check 기능의 경우 소요시간 측정 기능 추가
- 이로 인해 정상 상태인 경우 일 로그 크기가 18 MB -> 40 MB 수준으로 증가함
- 수정된 파일 : service, storage, system 정보 수집 관련 모듈
- CHG: 소스 파일 format 을 "서명없는 UTF-8" 형식으로 변경 처리함.
- 소스 및 기타 모든 파일을 변환 처리함.
Revision 1465
-------------------
수정일 : 2019-09-04
수정자 : 유희곤
- CHG: DNS resolve check 기능 보완 (#32786)
- 등록된 DNS 목록 중 죽은 DNS 가 존재할 경우.. 해당 장비 서비스 투입 불가
- 이에 등록된 DNS 에 대해 Alive check 를 수행하여 Alive DNS 만 추출 후
- Alive DNS 에 대해서만 resolve check 수행도록 수정
- 또한 DNS 목록 또는 Alive DNS 가 없는 경우.. 시스템에 설정된 기본 nameserver 를 통해 resolve check 하도록 수정
- 추가로 DNS resolve check 관련 로깅 보완
- 수정된 파일 : ServiceCheck.h ServiceCheck.cpp
Revision 1440
-------------------
수정일 : 2018-02-26
수정자 : 유희곤
- NEW: Storage Hang 감지를 위해 주기적인 Write/Read 테스트를 수행할 Storage Check Worker 기능 추가 (#31701)
- 독립 Worker 프로세스로 동작하며
- 설정상의 FILE_STORAGE_ROOT 하위의 경로에 대해 주기적인 파일 Write / Read 테스트 수행
- 공유메모리 상에 Check Start time 정보를 기록.
- 만약 Hang 걸린 경우 서비스 상태 check 모듈에서 Timeout 을 감지하여
- Service Disable 상태로 변경 처리.
- NEW: ZFS 상태 감시 기능 추가 (#31701)
- zpool list -Ho name,health 명령을 이용하여 content 저장 경로에 대한 health 정보 추출 후
- 상태에 따라 서비스 상태 변경 처리
- 서비스 상태 check 모듈에서 실제 작업 수행
- CHG: System 상태 정보 Hang check 기능 추가 (#31701)
- system 상태 정보 수집 시간을 정보를 이용하여
- service check 모듈에서 system 정보 수집 hang 여부 check
- hang 상황 탐지시 서비스 disable 상태 변경 처리
- CHG: 최신 System 상태 정보만 RCTS 로 전송되도록 수정 (#31701)
- RCTS 로 system 상태 정보 전송시 최신 정보만 전송되도록 수정
- 각각 수집, 전송은 독립 모듈로 10 sec 단위로 이루어지므로.. 서로 간섭 없음
- system hang 상황인 경우.. 잘못된 과거 정보 전송을 방지하기 위함.
- NEW: 서비스 상태의 Timeout 기능 추가 (#31701)
- RCTS 서비스 상태 전송 모듈에서 서비스 상태에 대한 Timeout Check 수행
- timeout 발생시 서비스 disable 상태로 변경하고 RCTS 로 전송
- 서비스 상태 check 모듈의 hang 상황을 탐지하고 RCTS 로 잘못된 상태 정보를 방지하기 위함
- NEW: Disk busy 상황시 자동 Read Only 상태 변경 (#31701)
- 서비스 상태 check 모듈에서
- disk busy 상태 정보를 판단하여... Disk busy 50 % 이상이고 queue 가 7 이상인 경우
- 자동으로 서비스 상태를 Read Only 로 변경 처리
- NEW: FHS 상태 감시를 위한 모니터링용 TCP port 제어 기능 추가 (#31701)
- conf 에 설정된 값을 기준으로
- 서비스 상태에 따라 TCP Port 를 listen, close 처리
- 이를 통해 서비스 불가 상황시 RC 대표 Domain 자동 해지, GMS 의 Hang 상황 관제 가능
- 다만 KT 에서 사용하는 정책라우팅 정책 등의 FHS Domain 관련 alive check 에 해당 TCP Port 감시를 할 경우..
DNS resolve 문제가 발생하여... 해당 장비 서비스 투입 불가하므로.. 절대 사용하지 말 것.
- NEW: service, system 상태 정보 RAW 정보 로깅 (#31701)
- 장애시 원인 분석 편의를 위해
- 공유메모리 정보를 주기적으로 1 row 형식으로 로깅 처리
- NEW: fhs.conf 상에 FHS_LISTEN_PORT_FOR_STATUS_MONITORING 항목 추가(#31701)
- FHS 의 상태 모니터링 관련 TCP Port 제어를 위한 Port 정보
- fhs.conf 의 [COMMON] 항목에 추가
- CHG: 수정된 fhs.conf 등록
- fhs.conf.in.R1440
Revision 1432
-------------------
수정일 : 2018-01-10
수정자 : 유희곤
- CHG: 메모리 사용량 관제 기능 추가를 위한 Free 메모리 추출 방식 수정 (#31360)
- 기존에는 free 용량만을 available memory 용량으로 추출
- 하지만.. 대부분 메모리 사용량 99~100% 으로 계산되어 유효하지 않음.
- available memory = free + inactive + cached 식으로 개선
- 수정소스: SystemInfo.cpp
Revision 1347
-------------------
수정일 : 2016-04-07
수정자 : 유희곤
- CHG: Apache alive check timeout 발생관련 check flow 수정(#27149)
- FreeBSD 6.2 에서 Apache alive check 실패가 자주 발생
- 이를 보완하기 위해 Apache alive check 실패 발생시 1 sec sleep 후 다시 check 하도록 기능 추가
- 수정소스: ServiceCheck.cpp
Revision 1219
-------------------
수정일 : 2015-09-10
수정자 : 유희곤
- NEW: 기존 vmasd 를 대체할 목적의 FHS 모니터링 모듈 개발
- 세부 내역: #22075 참고
- 설계서: svn://svc1svn.solbox.com/document/01 양방향/02 설계서/25 FHS 상태 정보 수집&전송/csagentd 설계서.vsdx
- 통신 프로토콜: svn://svc1svn.solbox.com/document/01 양방향/02 설계서/25 FHS 상태 정보 수집&전송/[csagentd] 통신프로토콜구조.xlsx
- NEW: SVN 신규 등록
- 버전 관리 정책에 따라 3.5.0 버전으로 신규 등록
+37
View File
@@ -0,0 +1,37 @@
#****************************************************************************
# Makefile for csagentd
# -----------------------------------------
#
# begin : 2015/04/13
# copyright : (C) 2005 Solbox Inc.
# author : Dev Storage team
# email : storage.sd@solbox.com
# version : 3.5
#
# CopyRight(C) 2005 Solbox Inc. All Rights reserved.
# Redistribution and use in source and binary forms, with or with out
# modification, are not permitted in outside of Solbox Inc.
#*****************************************************************************
SUBDIRS = lib src
.PHONY: all $(SUBDIRS)
all: $(SUBDIRS)
sync;
$(SUBDIRS):
$(MAKE) all -C $@
install:
@for dir in $(SUBDIRS); do (cd $$dir; $(MAKE) install); done
clean:
@for dir in $(SUBDIRS); do (cd $$dir; $(MAKE) clean); done
# End of Makefile
+44
View File
@@ -0,0 +1,44 @@
[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
# RCID
RCID = bd16
# File save directory path
FILE_STORAGE_ROOT = /stg
# RCTS info ( ip or domain )
RCTS_SERVER = 10.3.0.200
# FHS fimngd alive check port
FHS_PORT_MNGCHECK = 14000
# FHS alive status monitoring TCP port (control by csagentd)
# use default TCP 8083 when not config
FHS_LISTEN_PORT_FOR_STATUS_MONITORING = 8083
#-----------------------------------------------#
[csagentd]
# Log write level: 7:debug 6:info(Default) 5:notice 4:warning 3:error 2:critical 1:alert 0:emergency
#LOG_LEVEL = 6
# FHS service NIC name (ex: em0 or em0, em1, em2 )
SERVICE_NIC_NAME = em0, em1
# DNS list for Domain resolve check
# If vaule is empty, than dns resolve check not perform.
CHECK_DNS_LIST = ns.ktsh.co.kr, ns2.ktsh.co.kr, ns3.ktsh.co.kr, ns4.ktsh.co.kr, kns.kornet.net
# RCTS rc_mond listen tcp port
RCTS_PORT_INFO_SYSTEM = 14003
RCTS_PORT_INFO_SERVICE = 14004
#-----------------------------------------------#
+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;
}
+120
View File
@@ -0,0 +1,120 @@
/***************************************************************************
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 "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; }
// telegraf hang check 관련 socket 통신 처리 때문에.. protected -> public 으로 변경 처리.
//protected:
public:
/// @brief m_sock 로 부터 지정된 size 만큼 데이터 read 를 시도 ( read 함수와 동일 )
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
/// @param size [in] read 하고자 하는 데이터의 크기.
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
ssize_t Read( void * vptr, size_t size );
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기.
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
/// @param size [in] read 하고자 하는 데이터의 크기.
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
ssize_t ReadN( void * vptr, size_t size );
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
/// @param size [in] read 하고자 하는 데이터의 크기.
/// @param timeout [in] Timeout value (sec)
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
ssize_t ReadNTimeout( void * vptr, size_t size, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
///< 본 함수는 Socket 상에 이미 Data 가 존재하는 경우에만 사용.
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
/// @param size [in] read 하고자 하는 데이터의 크기.
/// @param timeout [in] Timeout value (sec)
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
ssize_t ReadNTimeout2( void * vptr, size_t size, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
public:
/// @brief m_sock 으로 지정된 크기만큼 vptr 의 데이터를 전송 시도.\n
///< Send Timeout 옵션 설정으로 Write Timeout 설정 가능. \n
///< 지정된 횟수만큼 재전송 실패시 오류로 처리함.
/// @param vptr [in] 전달할 데이터를 저장한 변수에 대한 포인터.
/// @param size [in] write 하고자 하는 데이터의 크기.
/// @return write 된 데이터의 크기. 0: fd closed, -1: 오류
ssize_t WriteN( const void * vptr, size_t size );
protected:
/// @brief 문자열로 전달받은 정보를 검사하여 IP 접속 정보를 생성한다.
unsigned int ConversionAddr( const char * name );
};
#endif /* __LIBRARY_BASE_SOCKET_H__ */
+215
View File
@@ -0,0 +1,215 @@
#include "Config.h"
#include <iostream>
#include <fstream>
#include <errno.h>
#include <stdlib.h>
#include <boost/algorithm/string.hpp> // use boost
Config::Config()
: m_keyDelimiter( CONFIG_DEFAULT_KEY_DELIMITER )
, m_valueDelimiter( CONFIG_DEFAULT_VALUE_DELIMITER )
{
}
/// @brief destructor
Config::~Config()
{
Clear();
}
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
/// @return On success return true, otherwise return false
bool Config::Open( const string path )
{
ifstream file;
// Config file open
file.open( path.c_str());
if( file.is_open() == false )
{
cerr << "[error] " << __FILE__<< ":" << __func__ << ": config file open failed.[" << path << "][" << strerror( errno ) << "]" << endl;
return false;
}
// 기존 데이터가 존재하면 삭제 처리.
if( m_configData.empty() == false )
m_configData.clear();
// config file parsing
string line;
string section;
vector< string > configs;
// 루프를 돌면서 Config 파일을 line 단위로 읽어들인당....
while( std::getline( file, line ) )
{
// 앞뒤 공백 제거 처리
boost::trim( line );
// 공백 or 주석처리 라인 검사
if( line.empty() == true || boost::starts_with( line, string("#") ) == true || line == "\r" )
{
continue;
}
// Section 여부 검사
if( boost::starts_with( line, string("[") ) == true && boost::ends_with( line, string("]") ) == true )
{
// [] 로 정의된 section 의 문자열 값 추출
line.erase( line.begin() );
line.erase( line.end() -1 );
boost::trim( line );
if( section.empty() == false && section != line )
{
// 이전 세션과 다른 신규 세션인 경우
// 기존까지 저장했던 데이터를 멤버 변수에 입력 처리 후 내부변수 초기화 처리.
m_configData.insert( make_pair( section, configs ) );
section.clear();
configs.clear();
}
// 신규 section 정보 저장처리.
section = line;
line.clear();
}
else
{
// Section 정보가 아닌 경우 => 실제 설정값 .. ^^
configs.push_back( line );
}
}
// 마지막 세션 처리된 정보가 존재하는 경우 멤버 변수에 저장 처리.
if( section.empty() == false )
{
m_configData.insert( make_pair( section, configs ) );
}
// 종료 처리.
file.close();
return true;
}
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
/// @return void
void Config::Clear()
{
m_configData.clear();
}
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
/// @return On success return true, otherwise return false
bool Config::GetConfig( const string section, const string key, string& value )
{
// Section 에 대한 임시 데이터 저장객체 생성
vector< string > configs;
// 해당 Section 이 존재하지 않는 경우
if( Find( section, configs ) == false )
return false;
string line;
string result;
vector< string >::const_iterator it;
// 해당 Key 이 존재하는지 검사
for( it = configs.begin(); it != configs.end(); it++)
{
line = *it;
// Line 상의 주석 제거
string::size_type pos = line.find( '#' );
if( pos != string::npos)
{
line = line.substr(0, pos);
boost::trim( line );
}
// key = value 에서 key 부분 추출
pos = line.find( m_keyDelimiter );
if( pos == string::npos )
{
continue;
}
else
{
result = line.substr(0, pos);
boost::trim( result );
}
if( key == result )
{
// Key 값이 동일한 경우
value = line.substr( pos+1 );
boost::trim( value );
return true;
}
}
return false;
}
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
/// @return On success return true, otherwise return false
bool Config::GetConfig( const string section, const string key, vector< string >& value )
{
string result;
vector< string > vec;
if( GetConfig( section, key, result ) == false )
return false;
// 전달받은 result 값을 value delimiter 를 이용하여 parsing 처리
boost::split( vec, result, boost::is_any_of( m_valueDelimiter ));
if( vec.empty() == true )
return false;
// 루프를 돌면서 trim 처리 후 결과값 저장처리.
vector< string >::const_iterator it;
for( it = vec.begin(); it != vec.end(); it++)
{
result = boost::trim_copy( *it );
if( result.empty() == false )
value.push_back( result );
}
if( value.empty() == true )
return false;
else
return true;
}
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
/// @param section [in] section 명
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
/// @return On success return true, otherwise return false
bool Config::Find( const string& section, vector<string>& configData )
{
map< string, vector< string > >::iterator it = m_configData.find( section );
if( it != m_configData.end() )
{
// 해당 section 을 찾은 경우
configData = it->second;
return true;
}
// 해당 section 을 찾지 못한 경우
return false;
}
+114
View File
@@ -0,0 +1,114 @@
/***************************************************************************
Config File Parser Class
-----------------------------------------
begin : 2010/02/27
copyright : (C) 2005 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __LIBRARY_CONFIG_H__
#define __LIBRARY_CONFIG_H__
#include <string>
#include <map>
#include <vector>
using namespace std;
#define CONFIG_DEFAULT_KEY_DELIMITER "="
#define CONFIG_DEFAULT_VALUE_DELIMITER ","
/// @brief Config class
/// 1. config 파일의 내용을 파싱처리하여 각 Section, Key 에 해당하는 값을 반환처리
/// 2. config 파일은 [section] 단위로 구분된다.
/// 3. config 파일은 key=value 로 구분가능하며 이는 Delimiter 설정을 통해 변경가능하다.
/// 4. value 값이 다중으로 존재하는 경우 "," 값을 통해 구분가능, Delimiter 변경시 다른 값도 사용가능함.
/// 5. config 파일상에서 "#" 로 시작하는 문자열은 라인 끝까지 주석으로 처리된다.
/// 6. 특정 Key 값에 대한 다중 value 값 조회는 vector<string> 을 통해 수행한다.
/// 7. 만약 다중 value 값이 존재시 string 으로 반환받을 경우 해당 Row 가 통째로 반환된다.
/// 8. 본 객체는 Open , Clear 함수가 호출되기전까지 이전 Config 정보가 저장된다.
class Config
{
private:
/// @brief config information saved variable
// string : section data
// vector<string> : key=value data
map< string, vector< string> > m_configData;
/// @brief Key, Value 구분자 저장 변수
string m_keyDelimiter;
string m_valueDelimiter;
public:
/// @brief constructor
Config();
/// @brief destructor
~Config();
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
/// @return On success return true, otherwise return false
bool Open( const string path );
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
/// @return void
void Clear();
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
/// @return On success return true, otherwise return false
bool GetConfig( const string section, const string key, string& value );
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
/// @return On success return true, otherwise return false
bool GetConfig( const string section, const string key, vector< string >& value );
/// @brief Key, Value Delimiter Get 함수
/// @param key [out] key delimiter 값
/// @param value [out] value delimiter 값
void GetDelimiter( string& key, string& value )
{
key = m_keyDelimiter;
value = m_valueDelimiter;
}
// BUG 2010-12-06 huibong
// string& value = CONFIG_DEFAULT_VALUE_DELIMITER 값은 문법상 오류 구문임.
// gcc 3.4.6 버전에서는 Compile 되나 gcc 4.4.5 에서는 error 로 처리되어 수정처리함.
/// @brief Key, Value Delimiter Set 함수
/// @param key [out] key delimiter 값
/// @param value [out] value delimiter 값, 지정하지 않을 경우 Default 값이 사용됨.
void SetDelimiter( string& key, string value = CONFIG_DEFAULT_VALUE_DELIMITER )
{
m_keyDelimiter = key;
m_valueDelimiter = value;
}
protected:
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
/// @param section [in] section 명
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
/// @return On success return true, otherwise return false
bool Find( const string& section, vector<string>& configData );
};
#endif /* __LIBRARY_CONFIG_H__ */
+487
View File
@@ -0,0 +1,487 @@
/***************************************************************************
Logger.cpp
-----------------------------------------
begin : 2011/10/26
copyright : (C) 2005 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include "Logger.h"
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <iostream>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define MAX_BUFFER_SIZE 2048 // 임시 버퍼 최대 크기
#define DEFAULT_LOG_LEVEL LINF
using namespace std;
CLogger* CLogger::m_pInstance = NULL;
bool CLogger::m_bIsInitialized = false;
pthread_mutex_t CLogger::m_mutex = PTHREAD_MUTEX_INITIALIZER;
CLogger::CLogger( string programName, string logDir, int logLevel )
{
m_szProgramName = programName;
m_szLogDir = logDir;
if( IsValidLogLevel( logLevel ) == true )
{
m_nLogLevel = logLevel;
}
else
{
m_nLogLevel = DEFAULT_LOG_LEVEL;
}
MakeLogLevelString();
}
void CLogger::MakeLogLevelString()
{
m_vectorLogLevelString.push_back( "EMR" );
m_vectorLogLevelString.push_back( "ALT" );
m_vectorLogLevelString.push_back( "CRT" );
m_vectorLogLevelString.push_back( "ERR" );
m_vectorLogLevelString.push_back( "WAR" );
m_vectorLogLevelString.push_back( "NOT" );
m_vectorLogLevelString.push_back( "INF" );
m_vectorLogLevelString.push_back( "DBG" );
m_vectorLogLevelString.push_back( "DEV" );
m_vectorLogLevelString.push_back( "DEV1" );
m_vectorLogLevelString.push_back( "DEV2" );
}
CLogger::~CLogger()
{
m_vectorLogLevelString.clear();
}
bool CLogger::Init( string programName, string logDir, int logLevel )
{
if( m_bIsInitialized == true )
{
return true;
}
pthread_mutex_lock( &m_mutex );
// 변수 유효성 검사.
if( programName.empty() == true || logDir.empty() == true )
{
pthread_mutex_unlock( &m_mutex );
return false;
}
if( IsValidLogLevel( logLevel ) == false )
{
pthread_mutex_unlock( &m_mutex );
return false;
}
if( MakeLogDir( programName, logDir ) == false )
{
pthread_mutex_unlock( &m_mutex );
return false;
}
if( CLogger::m_pInstance != NULL )
{
delete CLogger::m_pInstance;
CLogger::m_pInstance = NULL;
}
CLogger::m_pInstance = new CLogger( programName, logDir, logLevel );
m_bIsInitialized = true;
pthread_mutex_unlock( &m_mutex );
return true;
}
bool CLogger::IsValidLogLevel( int logLevel )
{
return ( ( logLevel < 0 || logLevel > MAX_LOG_LEVEL ) ? false : true );
}
bool CLogger::SetLogLevel( int logLevel )
{
if( IsValidLogLevel( logLevel ) == false )
{
return false;
}
m_nLogLevel = logLevel;
return true;
}
bool CLogger::MakeLogDir( string programName, string logDir )
{
string path = logDir + "/" + programName;
struct stat dirStat;
// 해당 이름을 가진 파일 또는 디렉토리가 존재하고
if( lstat( path.c_str(), &dirStat ) == 0 )
{
// 해당 이름이 디렉토리인 경우
if( S_ISDIR( dirStat.st_mode ) == true )
{
return true;
}
else
{
cerr << "exist file with the same name as log path(" << path << ")." << endl;
return false;
}
}
// 디렉토리 가 존재하지 않는 경우 디렉토리 생성 시도
string cmd = "mkdir -p " + path;
system( cmd.c_str() );
if( IsDirectory( path ) == false )
{
cerr << "can't make log directory. path=" << path << "." << endl;
return false;
}
return true;
}
bool CLogger::IsDirectory( string path )
{
struct stat dirStat;
if( lstat ( path.c_str(), &dirStat ) != 0 )
{
cerr << "Log path not valid. Check path [" << path << "][" << errno << "][" << strerror(errno) << "]" << endl;
return false;
}
// 해당 정보가 Directory 가 아닌 경우
if( S_ISDIR( dirStat.st_mode ) == false )
{
return false;
}
return true;
}
void CLogger::Exit()
{
if( m_bIsInitialized == false )
{
return;
}
pthread_mutex_lock( &m_mutex );
if( CLogger::m_pInstance != NULL )
{
delete CLogger::m_pInstance;
CLogger::m_pInstance = NULL;
m_bIsInitialized = false;
}
pthread_mutex_unlock( &m_mutex );
}
CLogger* CLogger::GetInstance()
{
return CLogger::m_pInstance;
}
string CLogger::GetLogFilename( struct tm &timeNow )
{
char timeStr[256];
snprintf( timeStr, (size_t)256, "%04d%02d%02d.log", timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday);
// Make File Name
string filename = m_szLogDir + "/" + m_szProgramName + "/" + m_szProgramName + "_" + string( timeStr );
return filename;
}
bool CLogger::Write( int logLevel, const char * fmt, ...)
{
// 가변 인자 처리
va_list args;
char buffer[MAX_BUFFER_SIZE];
va_start( args, fmt );
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
{
va_end( args );
return false;
}
va_end(args);
return Write( logLevel, PREFIX_DATE, NULL, 0, buffer );
}
bool CLogger::WriteNoPrefix( int logLevel, const char * fmt, ...)
{
// 가변 인자 처리
va_list args;
char buffer[MAX_BUFFER_SIZE];
va_start( args, fmt );
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
{
va_end( args );
return false;
}
va_end(args);
return Write( logLevel, PREFIX_NONE, NULL, 0, buffer );
return false;
}
// CHG 2012-08-16 huibong
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
// 함수명 : Write -> WriteWithFunc 으로 변경 처리
bool CLogger::WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
{
if( filename == NULL || funcname == NULL || lineNum < 0 || fmt == NULL )
{
return false;
}
// 가변 인자 처리
va_list args;
char buffer[MAX_BUFFER_SIZE];
va_start( args, fmt );
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
{
va_end( args );
return false;
}
va_end(args);
char className[255];
string funcPrefix = GetClassName( className, filename );
funcPrefix += "::" + string( funcname ) + "()";
return Write( logLevel, PREFIX_FUNCTION, funcPrefix.c_str(), lineNum, buffer );
}
const char* CLogger::GetClassName( char* className, const char* filename )
{
if( className == NULL )
{
cout << "[ERROR] The input argument 'className' is NULL." << endl;
return NULL;
}
if( filename == NULL )
{
cout << "[ERROR] The input argument 'filename' is NULL." << endl;
strcpy( className, "NULL" );
return className;
}
int filenameSize = strlen( filename );
memcpy( className, filename, filenameSize );
// find end position
char endCharacter = '.';
int endPos = filenameSize - 1;
for( ; endPos > 0; --endPos )
{
if( className[endPos] == endCharacter )
{
break;
}
}
if( endPos == 0 )
{
cout << "[ERROR] endPos=0" << endl;
strcpy( className, "NULL" );
return className;
}
className[endPos] = '\0';
// find start position
char startCharacter = '/';
int startPos = endPos - 1;
for( ; startPos > 0; --startPos )
{
if( className[startPos] == startCharacter )
{
break;
}
}
if( startPos != 0 )
{
startPos += 1;
}
return (className + startPos );
}
bool CLogger::Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log )
{
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
if( IsValidLogLevel( logLevel ) == false )
{
return false;
}
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
if( logLevel > m_nLogLevel )
{
return false;
}
if( log == NULL )
{
return false;
}
// Get Current Data & Time
time_t now = time( NULL );
struct tm timeNow;
localtime_r( &now, &timeNow );
string filename = GetLogFilename( timeNow );
// Log file open
FILE* pFile = NULL;
pFile = fopen( filename.c_str(), "a+" );
if( pFile == NULL )
{
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
return false;
}
// 시간 정보처리
char timeStr[256];
snprintf( timeStr, (size_t)256, "[%02d:%02d:%02d]"
, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec );
// Log Level 문자열 검색
string szLevel = m_vectorLogLevelString[ logLevel ];
switch( logPrefix )
{
case PREFIX_DATE:
/// 형식 예: [15:47:41] [DBG] sample log message.
fprintf( pFile, "%s [%-4s] %s\n", timeStr, szLevel.c_str(), log );
break;
case PREFIX_FUNCTION:
if( functionName == NULL )
{
fclose( pFile );
return false;
}
/// 형식 예: [15:47:41] [DBG] sample log message. [SomeClass::SomeFunction():12]
fprintf( pFile, "%s [%-4s] %s [%s:%d]\n", timeStr, szLevel.c_str(), log, functionName, lineNum );
break;
case PREFIX_NONE:
/// 형식 예: sample log message.
fprintf( pFile, "%s\n", log );
break;
}
// Write to log file
fflush( pFile );
// 종료 처리.
fclose( pFile );
return true;
}
bool CLogger::WriteHex( int logLevel, const unsigned char* data, const int size )
{
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
if( IsValidLogLevel( logLevel ) == false )
{
return false;
}
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
if( logLevel > m_nLogLevel )
{
return false;
}
if( data == NULL )
{
return false;
}
if( size <= 0 )
{
return false;
}
// Get Current Data & Time
time_t now = time( NULL );
struct tm timeNow;
localtime_r( &now, &timeNow );
string filename = GetLogFilename( timeNow );
// Log file open
FILE* pFile = NULL;
pFile = fopen( filename.c_str(), "a+" );
if( pFile == NULL )
{
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
return false;
}
for( int i = 0; i < size; ++i )
{
fprintf( pFile, "%02X", data[i] );
if( i == 0 )
{
continue;
}
if( ( (i+1) % 8 ) == 0 )
{
fprintf( pFile, " " );
}
if( ( (i+1) % 16 ) == 0 )
{
fprintf( pFile, " " );
}
if( ( (i+1) % 32 ) == 0 )
{
fprintf( pFile, "\n" );
}
}
fprintf( pFile, "\n" );
fflush( pFile );
// 종료 처리.
fclose( pFile );
return false;
}
+273
View File
@@ -0,0 +1,273 @@
/***************************************************************************
Logger.h
-----------------------------------------
begin : 2011/10/26
copyright : (C) 2005 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.2.0.805
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __LOGGER_H__
#define __LOGGER_H__
#include <string>
#include <vector>
#include <pthread.h>
///@brief 로그 레벨 정의
#define LEMR 0 /* system is or will be unusable if situation is not resolved */
#define LALT 1 /* immediate action required */
#define LCRT 2 /* critical situations */
#define LERR 3 /* error conditions */
#define LWAR 4 /* recoverable errors */
#define LNOT 5 /* unusual situation that merits investigation */
#define LINF 6 /* information messages */
#define LDBG 7 /* verbose data for debugging */
#define LDEV 8 /* verbose data for developer */
#define LDEV1 9 /* start or end the function of application level */
#define LDEV2 10 /* start or end the function of application level */
#define MAX_LOG_LEVEL LDEV2
///@brief Log 정보를 파일로 저장하기 위한 Class.
///1. 가변 format 으로 전달된 로그 관련 정보를 Log Level 에 따라 로그 파일에 아래의 4가지 형식으로 저장한다.
///
/// 1.1 Prefix로 [hh:mm:ss]와 [ClassName::FunctionName]이 추가된 로그
/// LOG( level, format, ... ) 매크로 사용.
/// 예) [17:43:40] [DBG] log level debug [LoggerTestTestLogger]
///
/// 1.2 Prefix로 [hh:mm:ss]이 추가된 로그
/// _LOG( level, format, ... ) 매크로 사용
/// 예) [17:43:40] [EMR] log level emergency
///
/// 1.3 Prefix가 없는 로그
/// _LOG_( level, format, ... ) 매크로 사용
/// 예) log level emergency
///
/// 1.4 Hex 로그.
/// LOG_HEX( level, data, size ) 매크로 사용
/// 예) 00010203 04050607 08090A0B 0C0D0E0F
///
///2. Logger 객체 초기화시 전달된 Log Level 정보보다 전달받은 Log Level 정보가 큰 경우 해당 로그는 파일로 저장되지 않는다.
///
///3. 로그 파일은 매 일단위로 저장파일이 변경된다.
///
///4. 로그 저장을 위한 program 경로가 존재하지 않는 경우 자동 생성 처리된다.
///
///5. 로그 저장방식은 매 저장로그마다 open-close 로 처리된다.
///
///6. 파일로 기록시 Log Level 에 대한 정보도 함께 기록된다.
///
///7. 싱글톤으로 작성되었고, CLogger::Init(...)시에 쓰레드 안정성을 제공한다.
///
///8. 동적으로 로그 레벨을 변경할 수 있는 인터페이스를 제공한다.
///
class CLogger
{
// Attributes
private:
///@brief 싱글톤 객체 인트턴스.
static CLogger* m_pInstance;
///@brief 싱글톤 객체 초기화 여부.
static bool m_bIsInitialized;
///@brief 싱글톤 객체 초기화시 스레드 안정성을 위한 뮤텍스.
static pthread_mutex_t m_mutex;
///@brief 프로그램 이름. 로그 파일 경로를 만들 때 사용.
std::string m_szProgramName;
///@brief 공통 로그 디렉토리 이름. 로그 파일 경로를 만들 때 사용.
std::string m_szLogDir;
///@brief 로그 레벨.
int m_nLogLevel;
///@brief 로그 레벨에 대응되는 문자열 정보를 저장.
std::vector<std::string> m_vectorLogLevelString;
protected:
public:
///@brief 로그 형식을 지정.
typedef enum
{
PREFIX_NONE = 0, /// 클래스 설명의 1.3에 해당
PREFIX_DATE, /// 클래스 설명의 1.2에 해당
PREFIX_FUNCTION, /// 클래스 설명의 1.1에 해당
} LOG_PREFIX;
// Operations
private:
///@brief 생성자.
/// 프로그램 이름, 공통 로그 디렉토리, 로그 레벨을 저장하고
/// 로그 레벨에 대응되는 문자열 정보를 만든다.
///@param programName [in] 프로그램 이름
///@param logDir [in] 공통 로그 디렉토리 경로
///@param logLevel [in] 로그 레벨
CLogger( std::string programName, std::string logDir, int logLevel );
///@brief 소멸자.
/// 로그 레벨에 대응되는 문자열 정보를 저장하고 있는
/// 벡터 m_vectorLogLevelString을 clear 시킴.
virtual ~CLogger();
///@brief 로그 레벨에 대응되는 문자열을 만든다.
///@param none.
///@return none.
void MakeLogLevelString();
///@brief 로그 파일이 위치할 실제 로그 디렉토리를 생성한다.
/// 생성할 디렉토리 경로는 'logDir/programName'이 된다.
///@param programName [in] 프로그램 이름
///@param logDir [in] 공통 로그 디렉토리 경로
///@return 디렉토리가 이미 존재하거나 생성 성공하면 true,
/// 해당 경로가 존재하지만 디렉토리가 아니거나, 디렉토리 생성 실패하면 false 반환.
static bool MakeLogDir( std::string programName, std::string logDir );
///@brief 해당 경로가 디렉토리 인지 아닌지 판단.
///@param path [in] 디렉토리 인지 아닌지 판단할 경로.
///@return 해당 경로가 디렉토이면 true,
/// 경로가 존재하지 않거나 디렉토리가 아니면 false 반환.
static bool IsDirectory( std::string path );
///@brief 로그 레벨이 올바른지 판별.
///@param logLevel [in]
///@return 올바른 로그 레벨이면 true, 그렇지 않으면 false 반환.
static bool IsValidLogLevel( int logLevel );
///@brief 시간 정보를 입력 받아 로그 파일 이름을 만든다.
/// 로그 파일 이름 형식 : 프로그램명_YYYYMMDD.log
///param timeNow [in] 현재 시간 정보.
///return 로그 파일 이름.
std::string GetLogFilename( struct tm &timeNow );
///@brief 로그를 남기는는 클래스가 정의된 파일의 이름에서 클래스명을 추출한다.
/// 쓰레드 안정성을 보장한다.
///@param className [out] 파일 이름에서 추출된 클래스명
///@param filename [in] 파일 이름.
///@return 클래스명 추출이 성공하면 클래스명 문자열의 포인터, 추출 실패하면 NULL.
const char* GetClassName( char* className, const char* filename );
///@brief 인자 logPrefix에 따라 적절한 형식으로 로그 파일에 로그를 저장한다.
/// 인자 logLevel이 설정된 로그 레벨보다 높으면 로그를 출력하지 않는다.
///@param logLevel [in] 로그 레벨
///@param logPrefix [in] 로그 프리픽스 종류.
///@functionName [in] ClassName::FunctionNmae() 형식의 문자열.
///@lineNum [in] 라인 번호.
///@log [in] 출력하고자 하는 로그 내용.
bool Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log );
protected:
public:
///@brief 싱글톤 객체 m_pInstance를 생성하고 인자 정보로 로그 디렉토리를 만든다.
///@param programName [in] 프로그램 이름
///@param logDir [in] 공통 로그 디렉토리 경로
///@param logLevel [in] 로그 레벨
///@return 로그 디렉토리를 만들고 싱글톤 객체를 생성했으면 true,
/// 로그 디렉토리를 만들지 못 했거나 인자 값이 올바르지 않으면 false 반환.
static bool Init( std::string programName, std::string logDir, int logLevel );
///@brief 싱글톤 객체 m_pInstance를 delete 한다.
///@param none.
///@return none.
static void Exit();
///@brief 싱글톤 객체 m_pInstance를 반환한다.
///@param none.
///@return CLogger 객체의 인스턴스.
static CLogger* GetInstance();
///@brief Prefix로 [hh:mm:ss]이 추가된 형식으로 로그 저장.
///@param logLevel [in] 로그 레벨
///@param fmt [in] 로그 내용 포맷.
///@param __VAR_ARGS__ [in] 가변 인자.
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
bool Write( int logLevel, const char * fmt, ...)
__attribute__((format(printf, 3, 4)));
///@brief Prefix가 없는 로그 저장.
///@param logLevel [in] 로그 레벨
///@param fmt [in] 로그 내용 포맷.
///@param __VAR_ARGS__ [in] 가변 인자.
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
bool WriteNoPrefix( int logLevel, const char * fmt, ...)
__attribute__((format(printf, 3, 4)));
// CHG 2012-08-16 huibong
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
///@brief Prefix로 [hh:mm:ss]와 [ClassName::FunctionName:line]이 추가된 형식으로 로그 저장.
///@param logLevel [in] 로그 레벨
///@param filename [in] 파일 이름
///@param funcname [in] 함수 이름
///@param lineNum [in] 라인 번호
///@param fmt [in] 로그 내용 포맷.
///@param __VAR_ARGS__ [in] 가변 인자.
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
bool WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
__attribute__((format(printf, 6, 7)));
///@brief 로그를 hex 형식으로 저장.
///@param logLevel [in] 로그 레벨
///@param data [in] hex 형식으로 출력할 데이터.
///@pram size [in] data의 크기.
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
bool WriteHex( int logLevel, const unsigned char* data, const int size );
///@brief 로그을 설정한다. 인자 logLevel이 적절한 값이면 새로운 값으로 변경하고
/// 적절한 값이 아니면 로그 레벨을 변경하지 않는다.
///@param logLevel [in] 설정할 로그 레벨
///@return none.
bool SetLogLevel( int logLevel );
inline int GetLogLevel() { return m_nLogLevel; };
inline std::string GetLogDir() { return m_szLogDir + "/" + m_szProgramName; };
};
#define LOG( level, format, ... ) \
if( CLogger::GetInstance() != NULL ) \
{ \
CLogger::GetInstance()->WriteWithFunc( level, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__ ); \
}
#define _LOG( level, format, ... ) \
if( CLogger::GetInstance() != NULL ) \
{ \
CLogger::GetInstance()->Write( level, format, ##__VA_ARGS__ ); \
}
#define _LOG_( level, format, ... ) \
if( CLogger::GetInstance() != NULL ) \
{ \
CLogger::GetInstance()->WriteNoPrefix( level, format, ##__VA_ARGS__ ); \
}
#define _LOG_HEX_( level, data, size ) \
if( CLogger::GetInstance() != NULL ) \
{ \
CLogger::GetInstance()->WriteHex( level, (const unsigned char*)data, size ); \
}
#define FUNC_BEGIN() LOG( LDEV1, "begin" )
#define FUNC_END() LOG( LDEV1, "end" )
#define FRM_BEGIN() LOG( LDEV2, "begin" )
#define FRM_END() LOG( LDEV2, "end" )
#endif // __LOGGER_H__
+61
View File
@@ -0,0 +1,61 @@
#****************************************************************************
# Makefile for Cloud Storage Common Libaray
# -----------------------------------------
#
# begin : 2015/04/13
# copyright : (C) 2013 Solbox Inc.
# author : Dev Storage Team
# email : storage.sd@solbox.com
# version : 3.5.0
#
# CopyRight(C) 2005 Solbox Inc. All Rights reserved.
# Redistribution and use in source and binary forms, with or with out
# modification, are not permitted in outside of Solbox Inc.
#*****************************************************************************
# Library info
LIB_NAME = InterCommon
LIB = lib$(LIB_NAME).a
OBJS = Config.o Logger.o BaseSocket.o
# Compiler info
CC = /usr/bin/g++
AR = /usr/bin/ar
DIR_INCLUDE = -I/usr/local/include
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized\
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
-D_REENTRANT -D_THREAD_SAFE -D_PTHREADS
############################
all:$(LIB)
sync
%.o: %.cpp
$(CC) $(CFLAGS) -o $@ -c $^ $(DIR_INCLUDE)
%.o: %.c
$(CC) $(CFLAGS) -o $@ -c $^ $(DIR_INCLUDE)
$(LIB): $(OBJS)
$(AR) crsv $@ $^
clean:
-rm -f *.o core *.out *.log
-rm -f $(LIB)
sync
install :
sync
# End of Makefile
+338
View File
@@ -0,0 +1,338 @@
#include "FhsShm.h"
#include "Logger.h"
#include <string.h>
#include <math.h>
// ShmDefine.h 에서 정의된 서비스 상태 코드 외에 미정의된 코드 처리를 위한 정의
#define SERVICE_STAT_NOT_DEFINE -1
// 생성자
CFhsShm::CFhsShm( struct FhsInfo * p )
: m_pShm( 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";
}
// 소멸자
CFhsShm::~CFhsShm()
{
}
// 멤버 변수인 공유메모리에 대한 포인터가 유효하지 여부를 판단하기 위한 함수.
bool CFhsShm::IsValid() const
{
if( m_pShm != NULL )
return true;
else
return false;
}
// 공유메모리 전체 영역을 초기화하기 위한 함수.
void CFhsShm::ClearAll() const
{
if( m_pShm != NULL )
{
memset( m_pShm, 0x00, sizeof( struct FhsInfo ) );
}
}
// 공유메모리 정보를 로그 파일에 기록 처리.
void CFhsShm::Print()
{
_LOG( LINF, "-- shared memory --" );
_LOG( LINF, "hostname : %s", m_pShm->system.hostname );
_LOG( LINF, "load_avg : %5.2lf, %5.2lf, %5.2lf", m_pShm->system.load_avg[0], m_pShm->system.load_avg[1], m_pShm->system.load_avg[2] );
_LOG( LINF, "memory usage : %3d %%, free %13ju byte, total %13ju byte", m_pShm->system.memory_usage, m_pShm->system.memory_free, m_pShm->system.memory_total );
_LOG( LINF, "swap usage : %3d %%, used %13ju byte, total %13ju byte", m_pShm->system.swap_usage, m_pShm->system.swap_used, m_pShm->system.swap_total );
_LOG( LINF, "disk max busy : %.1lf %%, max queue %ju", m_pShm->system.disk_max_busy, m_pShm->system.disk_max_queue );
_LOG( LINF, "storage usage : max %d %%, avg %d %%, avail %ju byte, total %ju byte"
, m_pShm->system.storage_max_usage
, ( int ) ceil( (( double ) (m_pShm->system.storage_total - m_pShm->system.storage_avail )) / m_pShm->system.storage_total * 100.0 )
, m_pShm->system.storage_avail, m_pShm->system.storage_total );
_LOG( LINF, "tcp session : %u, %u, %u (80_est, 80_total, total)", m_pShm->system.tcp_80_establish, m_pShm->system.tcp_80, m_pShm->system.tcp_total );
_LOG( LINF, "traffic in : %10ju byte/sec, packet %8ju count/sec", m_pShm->system.network_traffic_in, m_pShm->system.network_packet_in );
_LOG( LINF, "traffic out : %10ju byte/sec, packet %8ju count/sec", m_pShm->system.network_traffic_out, m_pShm->system.network_packet_out );
_LOG( LINF, "check time" );
_LOG( LINF, " last service check start : %ld", m_pShm->time_service_check_start_sec );
_LOG( LINF, " last system check start : %ld", m_pShm->time_system_check_start_sec );
_LOG( LINF, " last storage check ok : %ld", m_pShm->time_storage_check_ok_sec );
_LOG( LINF, "service stat : %s ( %d )", GetServiceStatString(), m_pShm->service.stat );
_LOG( LINF, "--------------" );
// 2018-02-08 NEW huibong 수집된 공유메모리 정보에 대한 시간별 분석이 용이하도록 해당 정보를 RAW 형식으로 로깅한다. (#31701)
_LOG( LINF, "RAW %s %.2lf %.2lf %.2lf"
" %d %ju %d %ju"
" %.1lf %ju %d %ju"
" %u %u %u %ju %ju"
" %ld %ld %ld"
, GetServiceStatString(), m_pShm->system.load_avg[0], m_pShm->system.load_avg[1], m_pShm->system.load_avg[2]
, m_pShm->system.memory_usage, m_pShm->system.memory_free, m_pShm->system.swap_usage, m_pShm->system.swap_used
, m_pShm->system.disk_max_busy, m_pShm->system.disk_max_queue, m_pShm->system.storage_max_usage, m_pShm->system.storage_avail
, m_pShm->system.tcp_80_establish, m_pShm->system.tcp_80, m_pShm->system.tcp_total, m_pShm->system.network_traffic_in, m_pShm->system.network_traffic_out
, m_pShm->time_service_check_start_sec, m_pShm->time_system_check_start_sec, m_pShm->time_storage_check_ok_sec
);
_LOG( LINF, "--------------" );
return;
}
// hostname
bool CFhsShm::SetHostname( const char * name ) const
{
// 전달받은 hostname 정보를 저장 처리한다.
// - hostname 정보는 규칙상 HOST_NAME_MAX_LENGTH 값만큼 저장 가능하므로...
// - HOST_NAME_MAX_LENGTH 길이 초과시... 저장 실패로 false 를 반환 처리한다.
if( name == NULL )
{
LOG( LERR, "[FhsShm] hostname is null point.");
return false;
}
if( strlen( name ) > HOST_NAME_MAX_LENGTH )
{
LOG( LERR, "[FhsShm] hostname[%s] too long. max length [%d]", name, HOST_NAME_MAX_LENGTH );
return false;
}
// hostname 이 중간에 변경될 경우.. 서비스에 영향을 미치게 되므로....
// 변경 여부를 로깅한다.
if( strlen( m_pShm->system.hostname ) != 0 )
{
if( strcmp( m_pShm->system.hostname, name ) != 0 )
{
_LOG( LWAR, "hostname changed.[%s] -> [%s]", m_pShm->system.hostname, name );
strcpy( m_pShm->system.hostname, name );
}
}
else
{
strcpy( m_pShm->system.hostname, name );
}
return true;
}
// loadaverage
bool CFhsShm::SetLoadaverage( const int & index, const double & value ) const
{
if( index > 2 )
{
LOG( LERR, "[FhsShm] loadaverage index[%d] not valid.", index );
return false;
}
m_pShm->system.load_avg[index] = value;
return true;
}
// memory total
void CFhsShm::SetMemoryTotal( const uint64_t & value ) const
{
m_pShm->system.memory_total = value;
return;
}
// memory free
void CFhsShm::SetMemoryFree( const uint64_t & value ) const
{
m_pShm->system.memory_free = value;
if( m_pShm->system.memory_total == 0 )
m_pShm->system.memory_usage = 0;
else
m_pShm->system.memory_usage = (int)ceil( (double)( m_pShm->system.memory_total - value ) * 100.0 / m_pShm->system.memory_total );
return;
}
// swap total, used
// - 사용률은 자체적으로 계산하여 처리한다.
void CFhsShm::SetSwapInfo( const uint64_t & total, const uint64_t & used ) const
{
m_pShm->system.swap_total = total;
m_pShm->system.swap_used = used;
if( total == 0 )
m_pShm->system.swap_usage = 0;
else
m_pShm->system.swap_usage = (int)ceil( (double)(used ) * 100.0 / total );
return;
}
// storage 용량, 사용률
void CFhsShm::SetStorageInfo( const uint64_t & total, const uint64_t & avail, const int & max_usage ) const
{
m_pShm->system.storage_total = total;
m_pShm->system.storage_avail = avail;
m_pShm->system.storage_max_usage = max_usage;
return;
}
// Disk max queuelength, max busy.
void CFhsShm::SetDiskInfo( const uint64_t & queue, const double & busy ) const
{
m_pShm->system.disk_max_queue = queue;
m_pShm->system.disk_max_busy = busy;
return;
}
// Network packet, traffic
void CFhsShm::SetNetworkInfo( const uint64_t & packetIn, const uint64_t & packetOut, const uint64_t & trafficIn, const uint64_t & trafficOut ) const
{
m_pShm->system.network_packet_in = packetIn;
m_pShm->system.network_packet_out = packetOut;
m_pShm->system.network_traffic_in = trafficIn;
m_pShm->system.network_traffic_out = trafficOut;
return;
}
// TCP 세션 정보
void CFhsShm::SetTcpInfo( const unsigned int & total, const unsigned int & tcp80, const unsigned int & tcp80Establish ) const
{
m_pShm->system.tcp_total = total;
m_pShm->system.tcp_80 = tcp80;
m_pShm->system.tcp_80_establish = tcp80Establish;
return;
}
// Service 상태 정보 설정.
bool CFhsShm::SetServiceStat( const int & stat )
{
if( stat < SERVICE_STAT_OK || stat > SERVICE_STAT_MAX )
{
LOG( LERR, "[FhsShm] service stat code[%d] not vaild. normal range: %d~%d", stat, SERVICE_STAT_OK, SERVICE_STAT_MAX );
return false;
}
else
{
// Service Stat 정보가 변경될 경우.. 이를 로깅 처리한다.
if( m_pShm->service.stat != stat )
{
// 상태 변경 처리.
int iOldStat = m_pShm->service.stat;
m_pShm->service.stat = stat;
_LOG( LINF, "[FhsShm] service stat change : %d (%s) -> %d (%s)"
, iOldStat, GetServiceStatString(iOldStat)
, m_pShm->service.stat, GetServiceStatString() );
}
}
return true;
}
// Service 상태 정보에 대한 문자열 반환.
const char * CFhsShm::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 * CFhsShm::GetServiceStatString()
{
std::map< int, std::string >::iterator pos;
pos = m_mapServiceStatString.find( m_pShm->service.stat );
if( pos != m_mapServiceStatString.end() )
{
// 해당 항목 존재시
return pos->second.c_str();
}
else
{
// 해당 항목이 존재하지 않는 경우.
return m_mapServiceStatString[SERVICE_STAT_NOT_DEFINE].c_str();
}
}
// Storage last check 시간 정보 업데이트
// 전달받은 인자값이 true 인 경우 시간 정보 설정, false 인 경우 -1 설정 처리함.
void CFhsShm::UpdateTimeStorageCheck( bool bCheckSuccess )
{
if( bCheckSuccess == true )
{
// storage check 성공시
// - 현재 시스템 기준 시간 정보 중 sec 정보를 계산하여 공유메모리에 저장 처리.
struct timespec currentClock;
memset( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
m_pShm->time_storage_check_ok_sec = currentClock.tv_sec;
}
else
{
// storage check 실패시
// time_storage_check_ok_sec 값의 정의 규약에 따라 -1 설정
// TODO: 우선 -1 로 설정하면 바로 Service Disable 처리 할 수 있지만...
// Disk write/read 관련 일시적인 이유로 실패할 수 있어
// 오류 상황에 대한 검증 없이 바로 서비스 disable 처리하면 오탐등으로 서비스에 문제가 발생할 수 있으므로...
// 우선은 -1 설정을 하지 않고.. expire 처리되도록 한다.
// -1 설정 활성화는 장애시 여러 data 를 분석하여.. write/read 오류에 대한 검증 후 진행한다.
//m_pShm->time_storage_check_ok_sec = -1;
}
return;
}
// Service 상태 Check Start 시간 정보 업데이트
void CFhsShm::UpdateTimeServiceCheck()
{
// 현재 시스템 기준 시간 정보 중 sec 정보를 계산하여 공유메모리에 저장 처리.
struct timespec currentClock;
memset( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
m_pShm->time_service_check_start_sec = currentClock.tv_sec;
return;
}
// System 상태 Check Start 시간 정보 업데이트
void CFhsShm::UpdateTimeSystemCheck()
{
// 현재 시스템 기준 시간 정보 중 sec 정보를 계산하여 공유메모리에 저장 처리.
struct timespec currentClock;
memset( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
m_pShm->time_system_check_start_sec = currentClock.tv_sec;
return;
}
+142
View File
@@ -0,0 +1,142 @@
/****************************************************************************
csagentd shared memory interface function class
-----------------------------------------
begin : 2015/04/21
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 __FHS_SHM_H__
#define __FHS_SHM_H__
#include "ShmDefine.h"
#include <string>
#include <map>
// 공유메모리 영역 전체에 대한 접근 인터페이스를 제공하기 위한 클래스
class CFhsShm
{
private:
// 공유메모리에 대한 포인터
struct FhsInfo * const m_pShm;
// 서비스 상태 code 에 대한 문자열 정보를 저장하기 위한 변수.
std::map<int, std::string> m_mapServiceStatString;
public:
// 생성자
CFhsShm( struct FhsInfo * p );
// 소멸자
~CFhsShm();
public:
// 함수 선언시 const 구문 사용 이유
// - 멤버변수인 m_pShm 값이 변경될 경우...엉뚱한 메모리 영역을 참조하는 불상사 발생.
// - 따라서 코딩시 m_pShm 값이 변경되지 않음을 선언하여....
// - 컴파일시 문제가 없는지 확인하기 위한 목적으로 사용함.
// - map 관련 멤버 변수 사용으로 인해.. 일부 함수에서는 사용하지 못함.
// 멤버 변수인 공유메모리에 대한 포인터가 유효하지 여부를 판단하기 위한 함수.
// return 유효한 경우 true, NULL 인 경우 false
bool IsValid( void ) const;
// 공유메모리 전체 영역을 초기화하기 위한 함수.
// return
void ClearAll( void ) const;
// 공유메모리 정보를 로그에 write
void Print( void ) ;
public:
// shared memory 각 항목에 대한 interface 함수
// hostname
bool SetHostname( const char * name ) const;
const char * GetHostName() { return m_pShm->system.hostname; }
// loadaverage
bool SetLoadaverage( const int & index, const double & value ) const;
// memory
void SetMemoryTotal( const uint64_t & value ) const;
void SetMemoryFree( const uint64_t & value ) const;
// swap
void SetSwapInfo( const uint64_t & total, const uint64_t & used ) const;
// storage 용량, 사용률
void SetStorageInfo( const uint64_t & total, const uint64_t & avail, const int & max_usage ) const;
int GetStorageMaxUsage() const { return m_pShm->system.storage_max_usage; }
uint64_t GetStorageTotal() const { return m_pShm->system.storage_total; }
// Disk max queuelength, max busy.
void SetDiskInfo( const uint64_t & queue, const double & busy ) const;
// Network packet, traffic
void SetNetworkInfo( const uint64_t & packetIn, const uint64_t & packetOut, const uint64_t & trafficIn, const uint64_t & trafficOut ) const;
// TCP 세션 정보
void SetTcpInfo( const unsigned int & total, const unsigned int & tcp80, const unsigned int & tcp80Establish ) const;
// Service 상태 정보 설정.
// false - 정의되지 않은 상태값인 경우...
bool SetServiceStat( const int & stat );
// Service 상태 정보 반환.
int GetServiceStat() { return m_pShm->service.stat; }
// Service 상태 정보에 대한 문자열 반환.
const char * GetServiceStatString( const int & stat );
// 현재 Service 상태 정보에 대한 문자열 반환.
const char * GetServiceStatString();
// 2018-02-07 NEW huibong Disk Busy 상황에서 Read Only 상태로 변경을 위한 Disk queue, busy 정보 반환 함수 추가 (#31701)
uint64_t GetDiskMaxQueue() const { return m_pShm->system.disk_max_queue; }
double GetDiskMaxBusy() const { return m_pShm->system.disk_max_busy; }
// 2018-01-31 NEW huibong 공유메모리 상에 신규로 추가 time 관련 처리를 위한 함수 추가 (#31701)
// Storage last check 시간 정보 업데이트
// 전달받은 인자값이 true 인 경우 시간 정보 설정, false 인 경우 -1 설정 처리함.
void UpdateTimeStorageCheck( bool bCheckSuccess );
// Service 상태 Check Start 시간 정보 업데이트
void UpdateTimeServiceCheck( void );
// System 상태 Check Start 시간 정보 업데이트
void UpdateTimeSystemCheck( void );
time_t GetTimeStorageCheck() const { return m_pShm->time_storage_check_ok_sec; }
time_t GetTimeServiceCheck() const { return m_pShm->time_service_check_start_sec; }
time_t GetTimeSystemCheck() const { return m_pShm->time_system_check_start_sec; }
public:
// RCTS 전송 처리 모듈에서 사용하기 위한 interface 함수.
const struct SystemInfo * GetSystemInfo() { return &(m_pShm->system); };
const struct ServiceStatus * GetServiceStatus() { return &( m_pShm->service ); };
};
#endif /* __FHS_SHM_H__ */
+996
View File
@@ -0,0 +1,996 @@
#include "Main.h"
#include "ProcessConfig.h"
#include "Logger.h"
#include "ShmDefine.h"
#include "ShmControl.h"
#include "FhsShm.h"
#include "WorkerSystemCollect.h"
#include "WorkerServiceCheck.h"
#include "WorkerStorageCheck.h"
#include "WorkerSend.h"
#include "WorkerMonitoringPortControl.h"
#include "WorkerTelegrafControl.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <time.h>
#include <iostream>
// Shared memory key 생성용 임시 파일.
// 2018-01-29 CHG huibong
// - /tmp 폴더에 대한 clean 모듈이 동작할 경우 자동 삭제되므로.. 경로 변경 처리 (#31701)
//#define FHS_SHARED_MEMORY_FILE "/tmp/"PROG_NAME".shm"
#define FHS_SHARED_MEMORY_FILE "/user/service/" PROG_NAME ".shm"
// 공유메모리 생성, 삭제 등의 Manager 객체에 대한 포인터.
CShmControl< struct FhsInfo > * g_pShmManager = NULL;
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
CFhsShm * g_pFhsShm = NULL;
// Worker 프로세스 중 SystemCollect Worker 프로세스의 PID 정보.
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리.
pid_t g_pidWorkerSystemCollect = -1;
// Worker 프로세스 중 ServiceCheck Worker 프로세스의 PID 정보.
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리.
pid_t g_pidWorkerServiceCheck = -1;
// Worker 프로세스 중 StorageCheck Worker 프로세스의 PID 정보.
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리.
pid_t g_pidWorkerStorageCheck = -1;
// Worker 프로세스 중 Send Worker 프로세스의 PID 정보.
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리.
pid_t g_pidWorkerSend = -1;
// Wokrker 프로세스 중 서비스 상태에 따른 Monitoring TCP Port Control 관련 Worker 프로세스 PID
pid_t g_pidWorkerMonitoringPortControl = -1;
// 2021-05-07 NEW huibong telegraf control 기능 추가 (#33542)
// Worker 프로세스 중 telegraf 모니터링, 재기동 처리를 담당하는 프로세스의 PID 정보
pid_t g_pidWorkerTelegrafControl = -1;
int main( int argc, char * argv[] )
{
// 설정 파일을 저장할 변수.
std::string strConfigFileName = DEFAULT_CONFIG_FILE;
// 전달 받은 옵션 여부 확인 및 처리
if( argc >= 2 )
{
int opt;
while( ( opt = getopt( argc, argv, "hvc:" ) ) != -1 ) // 옵션 끝까지 파싱처리
{
switch( opt )
{
case 'h':
case '?': // 정의되지 않은 문자가 나타날 경우 getopt 에 자동반환. 도움말 표시 처리.
PrintUsage();
return EXIT_SUCCESS;
case 'v':
PrintVersion();
return EXIT_SUCCESS;
case 'c':
strConfigFileName = optarg;
fprintf( stderr, PROG_NAME " start with user define conf[%s]\n", strConfigFileName.c_str() );
break;
default: // 본 조건절은 getopt 특성상 동작하지 않지만...업무 Flow 이해(?)를 위해 유지한다.
fprintf( stderr, "Unkonwn options[%c] used. program terminated.\n", opt );
return EXIT_FAILURE;
}
}
}
// 프로세스 중복 실행 여부 검사
if( IsCurrentProcessRun() == true )
{
fprintf( stderr, "[warning] Process [" PROG_NAME "] is already running....\n" );
return EXIT_FAILURE;
}
std::string szErrorMessage;
// conf 로딩 및 검사
if( CProcessConfig::Init( PROG_NAME, strConfigFileName, szErrorMessage ) == false )
{
fprintf( stderr, "[ERR] %s\n\n", szErrorMessage.c_str() );
return EXIT_FAILURE;
}
// Log 객체 생성 및 초기화
if( CLogger::Init( PROG_NAME, CProcessConfig::GetInstance()->GetLogPath(), LINF ) == false )
{
fprintf( stderr, "[ERR] Log module initialize failed.[%s]\n\n", CProcessConfig::GetInstance()->GetLogPath() );
return EXIT_FAILURE;
}
// Daemonize...
if( daemon( 1, 0 ) == -1 ) // nochdir: true(작업 디렉토리 변경 안함), noclose: false (표준 입출력, 에러를 /dev/null 로 리디렉트 처리)
{
fprintf( stderr, "[ERR] Process daemonize failed.[%s]\n\n", strerror( errno ) );
return EXIT_FAILURE;
}
// 공유메모리 생성 후 daemonize 처리시 문제
// - ipcs -ma 에 표시되는 CPID, LPID 에 daemonize 수행으로 인해 종료된 이전 process id 가 기록됨.
// - 이로 인해 정확한 상태를 판단하기 어려우므로.....
// - daemonzie 수행 후 공유메모리 생성, 접근을 수행하도록 한다.
// - 이로 인해 daemonizs 이후에는 fprintf 처리가 안되므로.. fprintf 구문은 주석 처리.
// 공유메모리 key
// - 다른 프로세스에서 사용하지 않으므로.. 고정키 값을 사용할 필요는 없다.
// - opendav 의 경우.. /tmp 상의 파일 생성 후 ftok() 함수를 이용하여 공유메모리 key 를 생성.
// - 따라서.. 고정 key 를 사용할 경우.. opendav 와 key 충돌 발생 우려가 있으므로...
// - opendav 와 동일한 방식으로 key 값을 생성하도록 처리한다.
// 공유메모리 key 생성 관련 기존 임시 파일 존재시 삭제 처리.
unlink( FHS_SHARED_MEMORY_FILE );
key_t iShmKey;
// 공유메모리 key 생성을 위한 임시 파일 생성 처리.
FILE * pShmFile = fopen( FHS_SHARED_MEMORY_FILE, "a+" );
if( pShmFile != NULL )
{
// Key file 생성이 성공한 경우... 파일만 있으면 되니까.. close 처리한다.
fclose( pShmFile );
pShmFile = NULL;
}
else
{
// Key file 생성에 실패한 경우...
int errorNum = errno;
LOG( LERR, "[Main] Shared memory key file create failed.[%s][%d][%s] => Process exit.", FHS_SHARED_MEMORY_FILE, errorNum, strerror( errorNum ) );
return EXIT_FAILURE;
}
// 임시 파일로 부터 key 값 추출
iShmKey = ftok( FHS_SHARED_MEMORY_FILE, 1 );
if( iShmKey == (key_t) -1 )
{
// 공유메모리 Key 생성 실패시
//fprintf( stderr, "[ERR] Shared memory key create failed. temp file[%s]\n\n", FHS_SHARED_MEMORY_FILE );
LOG( LERR, "[Main] Shared memory key create failed. key file[%s] => Process exit.", FHS_SHARED_MEMORY_FILE );
ReadyToExit();
return EXIT_FAILURE;
}
// 공유메모리에 생성 담당 class 객체를 생성 및 공유메모리 생성 or attach
CShmControl< struct FhsInfo > fhsShmManager;
if( fhsShmManager.Create( iShmKey, sizeof( struct FhsInfo ) ) == false )
{
//fprintf( stderr, "[ERR] Shared memory create failed. key[%ld] size[%lu]\n\n", iShmKey, sizeof( struct FhsInfo ) );
LOG( LERR, "[Main] Shared memory create failed. key[%ld] size[%lu] => Process exit.", iShmKey, sizeof( struct FhsInfo ) );
ReadyToExit();
return EXIT_FAILURE;
}
g_pShmManager = &fhsShmManager; // 전역 포인터에 대한 포인터 지정.
// 공유메모리에 데이터 입출력 처리를 담당할 class 객체 생성
CFhsShm fhsShm( fhsShmManager.GetShmPtr() );
if( fhsShm.IsValid() == false )
{
fhsShmManager.Remove();
//fprintf( stderr, "[ERR] Shared memory address not valid.\n\n" );
LOG( LERR, "[Main] Shared memory address not valid. => Process exit." );
ReadyToExit();
return EXIT_FAILURE;
}
g_pFhsShm = &fhsShm; // 전역 포인터에 대한 포인터 지정.
fhsShm.ClearAll(); // 공유메모리를 우선 초기화
fhsShm.SetServiceStat( SERVICE_STAT_OUT ); // 최초 기동시 서비스 상태는 수동 중지(1) 상태로 설정한다.
// signal 처리 설정
SetSignalMain();
// Process 기동 관련 정보 기록 -> Log
std::vector<std::string> vecTemp;
std::vector< std::string >::const_iterator it;
_LOG( LINF, "***********************************************************" );
_LOG( LINF, " %s Start. Version: %s", PROG_NAME, PROG_VERSION );
_LOG( LINF, "***********************************************************" );
_LOG( LINF, "Config : %s", strConfigFileName.c_str() );
_LOG( LINF, "Log : %s/%s", CProcessConfig::GetInstance()->GetLogPath(), PROG_NAME );
_LOG( LINF, "Log level : %d", CProcessConfig::GetInstance()->GetLogLevel() );
_LOG( LINF, "RC ID : %s", CProcessConfig::GetInstance()->GetRcId() );
_LOG( LINF, "Storage root : %s", CProcessConfig::GetInstance()->GetStorageRoot() );
_LOG( LINF, "Fimngd alive tcp port : %u", CProcessConfig::GetInstance()->GetFimngdCheckPort() );
_LOG( LINF, "Alive control listen tcp port : %u", CProcessConfig::GetInstance()->GetListenPortForStatusMonitoring() );
_LOG( LINF, "Service NIC" );
vecTemp = CProcessConfig::GetInstance()->GetSvcNic();
for( it = vecTemp.begin(); it != vecTemp.end(); it++ )
{
_LOG( LINF, " : %s", it->c_str() );
}
_LOG( LINF, "Check DNS List" );
vecTemp = CProcessConfig::GetInstance()->GetCheckDns();
for( it = vecTemp.begin(); it != vecTemp.end(); it++ )
{
_LOG( LINF, " : %s", it->c_str() );
}
_LOG( LINF, "RCTS");
_LOG( LINF, " Server address : %s", CProcessConfig::GetInstance()->GetRctsServer() );
_LOG( LINF, " System send tcp port : %u", CProcessConfig::GetInstance()->GetRctsSystemInfoPort() );
_LOG( LINF, " Service send tcp port : %u", CProcessConfig::GetInstance()->GetRctsServiceInfoPort() );
_LOG( LINF, "Shared Memory" );
_LOG( LINF, " Key : %ld", iShmKey );
_LOG( LINF, " Size : %lu", sizeof( struct FhsInfo ) );
_LOG( LINF, "***********************************************************" );
// Log Level 재설정. -> conf 설정대로 변경 처리.
#ifdef _DEBUG_
CLogger::GetInstance()->SetLogLevel( LDBG );
#else
CLogger::GetInstance()->SetLogLevel( CProcessConfig::GetInstance()->GetLogLevel() );
#endif
// Worker Process 생성
// FHS status 에 따른 GMS, Neocast 의 모니터링을 위한 TCP Port Control Worker 프로세스 생성
if( MakeWorkerMonitoringPortControl() == false )
{
LOG( LERR, "[Main] MonPortControl process create failed. => Process exit." );
ReadyToExit();
return EXIT_FAILURE;
}
// Worker Process 생성
// System 정보 Collect 처리를 담당할 SystemCollect Worker 프로세스 생성
if( MakeWorkerSystemCollect() == false )
{
LOG( LERR, "[Main] SystemCollect process create failed. => Process exit." );
ReadyToExit();
return EXIT_FAILURE;
}
// Storage 상태 Check 를 담당할 StorageCheck Worker 프로세스 생성
if( MakeWorkerStorageCheck() == false )
{
LOG( LERR, "[Main] StorageCheck process create failed. => Process exit." );
ReadyToExit();
return EXIT_FAILURE;
}
// Service 상태 Check 를 담당할 ServiceCheck Worker 프로세스 생성
if( MakeWorkerServiceCheck() == false )
{
LOG( LERR, "[Main] ServiceCheck process create failed. => Process exit." );
ReadyToExit();
return EXIT_FAILURE;
}
// RCTS 로 System, Service 상태 정보를 전송할 Send Worker 프로세스 생성
if( MakeWorkerSend() == false )
{
LOG( LERR, "[Main] Send process create failed. => Process exit." );
ReadyToExit();
return EXIT_FAILURE;
}
// telegraf control 을 담당할 worker 프로세스 생성
if( MakeWorkerTelegrafControl() == false )
{
LOG( LERR, "[Main] TelegrafControl process create failed. => Process exit." );
ReadyToExit();
return EXIT_FAILURE;
}
// Process Name 처리
setproctitle( "Main" );
// Main Process : 그냥 대기
while( 1 )
{
// Main Process 는 할 일이 없다.
// => Worker 프로세스 종료시 SIGCHLD 신호로 인해 신호처리기에서 Worker Process 재성성 수행함.
// => 따라서 그냥 시그널 대기
pause();
}
// 다음의 코드는 Daemon 으로 동작하기 때문에 수행되지 않는다.
ReadyToExit();
return EXIT_SUCCESS;
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExit()
{
// 공유메모리 key 생성에 사용된 임시 파일 삭제
unlink( FHS_SHARED_MEMORY_FILE );
// 공유메모리 제거
// - 다른 프로세스에서 사용할 것이 아니므로.. 종료시 삭제 처리한다.
if( g_pShmManager != NULL )
g_pShmManager->Remove();
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// 사용방법 표시
void PrintUsage()
{
fprintf( stderr, "\n" );
fprintf( stderr, "Usage: " PROG_NAME " [-h] [-v] [-c {file}] \n" );
fprintf( stderr, "Options: \n" );
fprintf( stderr, " -h Display help information \n" );
fprintf( stderr, " -v Display version \n" );
fprintf( stderr, " -c {file} Use {file} as config file \n" );
fprintf( stderr, "\n" );
fprintf( stderr, PROG_NAME " is Solbox Cloud Storage module.\n" );
fprintf( stderr, " - System status collect periodically and transfer to RCTS.\n" );
fprintf( stderr, " - Service status check periodically and transfer to RCTS.\n" );
fprintf( stderr, " - Storage status check periodically.\n" );
fprintf( stderr, " - TCP port listen and control for FHS status monitoring.\n" );
fprintf( stderr, " - Telegraf hang check and auto restart.\n\n" );
return;
}
// 버전 정보 표시
void PrintVersion()
{
fprintf( stderr, "\n" );
fprintf( stderr, PROG_NAME " version: " PROG_VERSION "\n\n" );
return;
}
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
bool IsCurrentProcessRun( )
{
char tempBuffer[512];
FILE * fd = NULL;
bool bRun = false;
snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -x %s | sort", PROG_NAME );
fd = popen( tempBuffer, "r" );
if( fd == NULL )
{
std::cerr << "[error] Process duplication check failed.[popen error][" << strerror( errno ) << "]" << std::endl;
// 오류 발생시 true 반환하여 프로세스 실행 방지처리
return true;
}
else
{
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
while( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
{
std::string tempPid( tempBuffer );
Trim( tempPid );
if( atoi( tempPid.c_str() ) != getpid() )
{
std::cout << "[info] Process duplication found. pid[" << atoi( tempPid.c_str() ) << "]" << std::endl;
bRun = true;
break;
}
}
pclose( fd );
return bRun;
}
}
/// @brief std 상에 trim 함수가 없어서 직접 구현. 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
/// @return void
void Trim( std::string & str )
{
if( str.length() == 0 )
return;
// 문자열 뒤의 공백, TAB, CR 등의 문자 제거처리.
std::string::size_type pos = str.find_last_not_of( " \a\b\f\n\r\t\v" );
if( pos != std::string::npos )
str.erase( pos + 1 );
// 문자열 앞의 공백, TAB, CR 등의 문자 제거처리.
pos = str.find_first_not_of( " \a\b\f\n\r\t\v" );
if( pos != std::string::npos )
str.erase( 0, pos );
}
// Worker 프로세스 종료 신호 수신시
static void SignalWorkerDead( int nSignalNumber )
{
pid_t deadPid;
int nDeadStatus;
while( ( deadPid = waitpid( -1, &nDeadStatus, WNOHANG ) ) > 0 )
{
// Worker 프로세스가 signal 에 의해 종료되었는지 검사.
// - 종료 사유 로깅 및
// - Worker 의 초기화 실패로 인한 종료시... Main 프로세스가 종료되도록 처리.
if( WIFSIGNALED( nDeadStatus ) )
{
// signal 에 의해 종료된 경우...
// - 관련 내역 로깅
if( deadPid == g_pidWorkerSystemCollect )
{
_LOG( LWAR, "[Main] SystemCollect process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
}
else if( deadPid == g_pidWorkerStorageCheck )
{
_LOG( LWAR, "[Main] StorageCheck process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
}
else if( deadPid == g_pidWorkerServiceCheck )
{
_LOG( LWAR, "[Main] ServiceCheck process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
}
else if( deadPid == g_pidWorkerSend )
{
_LOG( LWAR, "[Main] Send process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
}
else if( deadPid == g_pidWorkerMonitoringPortControl )
{
_LOG( LWAR, "[Main] MonPortControl process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
}
else if( deadPid == g_pidWorkerTelegrafControl )
{
_LOG( LWAR, "[Main] TelegrafControl process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
}
else
{
_LOG( LWAR, "[Main] Unknown child process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
}
}
else
{
// signal 에 종료된 상황이 아닌 경우...
// - 각 Worker 의 초기화 실패 발생시... return 구문에 의해 종료됨.
// - 기타 등등.
// 관련 내역 로깅.
if( deadPid == g_pidWorkerSystemCollect )
{
_LOG( LWAR, "[Main] SystemCollect process[%d] killed. Not signal.", deadPid );
}
else if( deadPid == g_pidWorkerStorageCheck )
{
_LOG( LWAR, "[Main] StorageCheck process[%d] killed. Not signal.", deadPid );
}
else if( deadPid == g_pidWorkerServiceCheck )
{
_LOG( LWAR, "[Main] ServiceCheck process[%d] killed. Not signal.", deadPid );
}
else if( deadPid == g_pidWorkerSend )
{
_LOG( LWAR, "[Main] Send process[%d] killed. Not signal", deadPid );
}
else if( deadPid == g_pidWorkerMonitoringPortControl )
{
_LOG( LWAR, "[Main] MonPortControl process[%d] killed. Not signal", deadPid );
}
else if( deadPid == g_pidWorkerTelegrafControl )
{
_LOG( LWAR, "[Main] TelegrafControl process[%d] killed. Not signal", deadPid );
}
else
{
_LOG( LWAR, "[Main] Unknown child process[%d] killed. Not signal", deadPid );
}
// 만약 Worker 프로세스들이 초기화 실패로 인해 EXIT_FAILURE 반환되는 상황이라면...
// 자식 프로세스를 재생성하지 않고.. 프로세스를 종료 처리한다.
if( WIFEXITED( nDeadStatus ) )
{
if( WEXITSTATUS( nDeadStatus ) == EXIT_FAILURE )
{
if( deadPid == g_pidWorkerSystemCollect )
{
_LOG( LWAR, "[Main] SystemCollect process[%d] initialize failed.", deadPid );
}
else if( deadPid == g_pidWorkerStorageCheck )
{
_LOG( LWAR, "[Main] StorageCheck process[%d] initialize failed.", deadPid );
}
else if( deadPid == g_pidWorkerServiceCheck )
{
_LOG( LWAR, "[Main] ServiceCheck process[%d] initialize failed.", deadPid );
}
else if( deadPid == g_pidWorkerSend )
{
_LOG( LWAR, "[Main] Send process[%d] initialize failed.", deadPid );
}
else if( deadPid == g_pidWorkerMonitoringPortControl )
{
_LOG( LWAR, "[Main] MonPortControl process[%d] initialize failed.", deadPid );
}
else if( deadPid == g_pidWorkerTelegrafControl )
{
_LOG( LWAR, "[Main] TelegrafControl process[%d] initialize failed.", deadPid );
}
else
{
_LOG( LWAR, "[Main] Unknown child process[%d] exit with fail.", deadPid );
}
_LOG( LERR, "[Main] Program abnormally exited because Worker initialize faild.." );
// Main 종료 시그널 발생
raise( SIGTERM );
continue;
}
}
}
// 위에서 로깅 및 초기화 실패로 인한 Main 종료 처리 작업을 수행했으므로...
// 죽은 Worker 프로세스를 재생성 처리한다.
if( deadPid == g_pidWorkerSystemCollect )
{
if( MakeWorkerSystemCollect() == false )
{
_LOG( LERR, "[Main] SystemCollect process recreate failed.=> Main process exit.");
raise( SIGTERM );
}
else
{
_LOG( LWAR, "[Main] SystemCollect process recreate success." );
}
}
else if( deadPid == g_pidWorkerStorageCheck )
{
if( MakeWorkerStorageCheck() == false )
{
_LOG( LERR, "[Main] StorageCheck process recreate failed.=> Main process exit." );
raise( SIGTERM );
}
else
{
_LOG( LWAR, "[Main] StorageCheck process recreate success." );
}
}
else if( deadPid == g_pidWorkerServiceCheck )
{
if( MakeWorkerServiceCheck() == false )
{
_LOG( LERR, "[Main] ServiceCheck process recreate failed.=> Main process exit." );
raise( SIGTERM );
}
else
{
_LOG( LWAR, "[Main] ServiceCheck process recreate success." );
}
}
else if( deadPid == g_pidWorkerSend )
{
if( MakeWorkerSend() == false )
{
_LOG( LERR, "[Main] Send process recreate failed.=> Main process exit." );
raise( SIGTERM );
}
else
{
_LOG( LWAR, "[Main] Send process recreate success." );
}
}
else if( deadPid == g_pidWorkerMonitoringPortControl )
{
if( MakeWorkerMonitoringPortControl() == false )
{
_LOG( LERR, "[Main] MonPortControl process recreate failed.=> Main process exit." );
raise( SIGTERM );
}
else
{
_LOG( LWAR, "[Main] MonPortControl process recreate success." );
}
}
else if( deadPid == g_pidWorkerTelegrafControl )
{
if( MakeWorkerTelegrafControl() == false )
{
_LOG( LERR, "[Main] TelegrafControl process recreate failed.=> Main process exit." );
raise( SIGTERM );
}
else
{
_LOG( LWAR, "[Main] TelegrafControl process recreate success." );
}
}
}
// 오류 발생시 해당 내역 로깅
if( deadPid < 0 )
{
int errorNum = errno;
LOG( LERR, "[Main] SIG_CHLD receive but waitpid return error[%d][%s]", errorNum, strerror( errorNum ) );
}
return;
}
// Main 프로세스에 대한 종료 처리 수신시
static void SignalMainTerminate( int nSignalNumber )
{
// 자식 프로세스를 종료 처리한다.
if( g_pidWorkerMonitoringPortControl != -1 )
{
kill( g_pidWorkerMonitoringPortControl, SIGTERM );
}
if( g_pidWorkerSystemCollect != -1 )
{
kill( g_pidWorkerSystemCollect, SIGTERM );
}
if( g_pidWorkerStorageCheck != -1 )
{
kill( g_pidWorkerStorageCheck, SIGTERM );
}
if( g_pidWorkerServiceCheck != -1 )
{
kill( g_pidWorkerServiceCheck, SIGTERM );
}
if( g_pidWorkerSend != -1 )
{
kill( g_pidWorkerSend, SIGTERM );
}
if( g_pidWorkerTelegrafControl != -1 )
{
kill( g_pidWorkerTelegrafControl, SIGTERM );
}
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[Main:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[Main:%d] Process exit by abnormal signal[%d]. Good Bye..", getpid(), nSignalNumber );
}
// 기타 모듈들의 종료 처리 수행
ReadyToExit();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Main Process 의 signal 처리기...
// fork 된 worker 프로세스 역시 기본적으로 본 signal 처리 action 을 상속받는다.
void SetSignalMain()
{
sigset_t set;
struct sigaction act;
memset( &act, 0x00, sizeof( act ) );
sigfillset( &set );
sigprocmask( SIG_SETMASK, &set, NULL );
sigfillset( &act.sa_mask );
// 무시 처리 signal
act.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &act, NULL ); /* desciptor 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
sigaction( SIGHUP, &act, NULL ); /* process를 기동시킨 관리자의 로그아웃시, 또는 config reload 등의 처리 signal*/
sigaction( SIGINT, &act, NULL ); /* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
sigaction( SIGQUIT, &act, NULL ); /* 키보드에 의한 Abort 신호 처리 => ? */
// Worker child process 종료에 대한 처리기 설정.
act.sa_handler = SignalWorkerDead;
sigaction( SIGCHLD, &act, NULL );
// 사용자의 종료 또는 에러 관련 신호 처리.
act.sa_handler = SignalMainTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
// System 정보 Collect 처리를 담당할 SystemCollect Worker 프로세스 생성
bool MakeWorkerSystemCollect()
{
// fork() 결과를 SystemCollect Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장
g_pidWorkerSystemCollect = fork();
if( g_pidWorkerSystemCollect == 0 )
{
// 생성된 Worker 프로세스인 경우...
int iResult;
// Worker SystemCollect 프로세스의 Main 함수 호출
iResult = WorkerSystemCollectMain();
// Worker SystemCollect 프로세스의 Main 함수 종료시 종료 처리
_LOG( LINF, "[Main] SystemCollect process exit. PID[%d] return[%d]", getpid(), iResult );
ReadyToExit();
exit( iResult );
}
else if( g_pidWorkerSystemCollect < 0 )
{
// fork 실패시
int errorNum = errno;
LOG( LERR, "[Main] SystemCollect process create failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
else
{
// 현재의 Main 프로세스
_LOG( LINF, "[Main] SystemCollect process created.. PID[%d]", g_pidWorkerSystemCollect );
// 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 200000000; // 0.2 sec
nanosleep( &sleep, NULL );
}
return true;
}
// Service 상태 Check 를 담당할 ServiceCheck Worker 프로세스 생성
bool MakeWorkerServiceCheck()
{
// fork() 결과를 ServiceCheck Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장.
g_pidWorkerServiceCheck = fork();
if( g_pidWorkerServiceCheck == 0 )
{
// 생성된 Worker 프로세스인 경우...
int iResult;
// Worker ServiceCheck 프로세스의 Main 함수 호출
iResult = WorkerServiceCheckMain();
// Worker ServiceCheck 프로세스의 Main 함수 종료시 종료 처리
_LOG( LINF, "[Main] ServiceCheck process exit. PID[%d] return[%d]", getpid(), iResult );
ReadyToExit();
exit( iResult );
}
else if( g_pidWorkerServiceCheck < 0 )
{
// fork 실패시
int errorNum = errno;
LOG( LERR, "[Main] ServiceCheck process create failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
else
{
// 현재의 Main 프로세스
_LOG( LINF, "[Main] ServiceCheck process created.. PID[%d]", g_pidWorkerServiceCheck );
// 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 200000000; // 0.2 sec
nanosleep( &sleep, NULL );
}
return true;
}
// RCTS 로 System, Service 상태 정보를 전송할 Send Worker 프로세스 생성
bool MakeWorkerSend()
{
// fork() 결과를 Send Worker 프로세스의 PID 정보 저장 목적 전역변수에 저장.
g_pidWorkerSend = fork();
if( g_pidWorkerSend == 0 )
{
// 생성된 Worker 프로세스인 경우...
int iResult;
// Worker Send 프로세스의 Main 함수 호출
iResult = WorkerSendMain();
// Worker Send 프로세스의 Main 함수 종료시 종료 처리
_LOG( LINF, "[Main] Send process exit. PID[%d] return[%d]", getpid(), iResult );
ReadyToExit();
exit( iResult );
}
else if( g_pidWorkerSend < 0 )
{
// fork 실패시
int errorNum = errno;
LOG( LERR, "[Main] Send process create failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
else
{
// 현재의 Main 프로세스
_LOG( LINF, "[Main] Send process created.. PID[%d]", g_pidWorkerSend );
// 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 200000000; // 0.2 sec
nanosleep( &sleep, NULL );
}
return true;
}
// Storage 상태 Check 를 담당할 StorageCheck Worker 프로세스 생성
bool MakeWorkerStorageCheck()
{
// fork() 결과를 StorageCheck Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장.
g_pidWorkerStorageCheck = fork();
if( g_pidWorkerStorageCheck == 0 )
{
// 생성된 Worker 프로세스인 경우...
int iResult;
// Worker StorageCheck 프로세스의 Main 함수 호출
iResult = WorkerStorageCheckMain();
// Worker StorageCheck 프로세스의 Main 함수 종료시 종료 처리
_LOG( LINF, "[Main] StorageCheck process exit. PID[%d] return[%d]", getpid(), iResult );
ReadyToExit();
exit( iResult );
}
else if( g_pidWorkerStorageCheck < 0 )
{
// fork 실패시
int errorNum = errno;
LOG( LERR, "[Main] StorageCheck process create failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
else
{
// 현재의 Main 프로세스
_LOG( LINF, "[Main] StorageCheck process created.. PID[%d]", g_pidWorkerStorageCheck );
// 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 200000000; // 0.2 sec
nanosleep( &sleep, NULL );
}
return true;
}
// TCP Port 를 Listen 상태로 Open 후 서비스 상태에 따른 Port 상태 제어를 담당할 Worker 프로세스 생성
bool MakeWorkerMonitoringPortControl()
{
// fork() 결과를 MonPortControl Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장.
g_pidWorkerMonitoringPortControl = fork();
if( g_pidWorkerMonitoringPortControl == 0 )
{
// 생성된 Worker 프로세스인 경우...
int iResult;
// Worker MonPortControl 프로세스의 Main 함수 호출
iResult = WorkerMonPortControlMain();
// Worker MonPortControl 프로세스의 Main 함수 종료시 종료 처리
_LOG( LINF, "[Main] MonPortControl process exit. PID[%d] return[%d]", getpid(), iResult );
ReadyToExit();
exit( iResult );
}
else if( g_pidWorkerMonitoringPortControl < 0 )
{
// fork 실패시
int errorNum = errno;
LOG( LERR, "[Main] MonPortControl process create failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
else
{
// 현재의 Main 프로세스
_LOG( LINF, "[Main] MonPortControl process created.. PID[%d]", g_pidWorkerMonitoringPortControl );
// 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 200000000; // 0.2 sec
nanosleep( &sleep, NULL );
}
return true;
}
// Telegarf control 을 담당할 worker 프로세스 생성
bool MakeWorkerTelegrafControl()
{
// fork() 결과를 TelegrafControl Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장.
g_pidWorkerTelegrafControl = fork();
if( g_pidWorkerTelegrafControl == 0 )
{
// 생성된 Worker 프로세스인 경우...
int iResult;
// Worker TelegrafControl 프로세스의 Main 함수 호출
iResult = WorkerTelegrafControlMain();
// Worker TelegrafControl 프로세스의 Main 함수 종료시 종료 처리
_LOG( LINF, "[Main] TelegrafControl process exit. PID[%d] return[%d]", getpid(), iResult );
ReadyToExit();
exit( iResult );
}
else if( g_pidWorkerTelegrafControl < 0 )
{
// fork 실패시
int errorNum = errno;
LOG( LERR, "[Main] TelegrafControl process create failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
else
{
// 현재의 Main 프로세스
_LOG( LINF, "[Main] TelegrafControl process created.. PID[%d]", g_pidWorkerTelegrafControl );
// 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 200000000; // 0.2 sec
nanosleep( &sleep, NULL );
}
return true;
}
+85
View File
@@ -0,0 +1,85 @@
/****************************************************************************
Main.h for csagentd
-----------------------------------------
begin : 2015/04/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 __CS_AGENTD_MAIN_H__
#define __CS_AGENTD_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 System 정보 Collect 처리를 담당할 SystemCollect Worker 프로세스 생성
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerSystemCollect();
/// @brief Service 상태 Check 를 담당할 ServiceCheck Worker 프로세스 생성
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerServiceCheck();
/// @brief RCTS 로 System, Service 상태 정보를 전송할 Send Worker 프로세스 생성
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerSend();
/// @brief Storage 상태 Check 를 담당할 StorageCheck Worker 프로세스 생성
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerStorageCheck();
/// @brief TCP Port 를 Listen 상태로 Open 후 서비스 상태에 따른 Port 상태 제어를 담당할 Worker 프로세스 생성
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerMonitoringPortControl();
// Telegarf control 을 담당할 worker 프로세스 생성
bool MakeWorkerTelegrafControl();
#ifdef __cplusplus
}
#endif
#endif /* __CS_AGENTD_MAIN_H__ */
+81
View File
@@ -0,0 +1,81 @@
#****************************************************************************
# Makefile for csagentd
# -----------------------------------------
#
# begin : 2015/04/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.
#****************************************************************************
# Program info
PROG_NAME = csagentd
REVISION = 1539
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
DEFAULT_CONFIG_FILE = /user/service/etc/fhs.conf
INSTALL_BIN = /user/service/bin
INSTALL_CONF = /user/service/etc
# Compiler info
CC = /usr/bin/g++
CFLAGS = -Wall -O2 -g -Wreturn-type -Wunused -Wuninitialized\
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
-fno-rtti -D_REENTRANT -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
LFLAGS =
DFLAGS = -DPROG_NAME=\"$(PROG_NAME)\" -DPROG_VERSION=\"$(PROG_VERSION)\" -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\"
# Application Enviroment
APP = $(PROG_NAME)
DIR_INCLUDE = -I../lib
DIR_LIB = -L../lib
OBJ = ProcessConfig.o FhsShm.o StorageInfo.o \
SystemInfo.o ServiceCheck.o RctsSocket.o StorageCheck.o TelegrafControl.o \
WorkerSystemCollect.o WorkerServiceCheck.o WorkerSend.o WorkerStorageCheck.o \
WorkerMonitoringPortControl.o WorkerTelegrafControl.o \
Main.o
LIBS = ../lib/libInterCommon.a -lpthread -lkvm -lgeom -ldevstat -lc -lm
#---------------------------------------------------------------------#
all:$(APP)
sync
%.o: %.cpp
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
$(PROG_NAME): $(OBJ)
$(CC) $(LFLAGS) -o $@ $^ $(DFLAGS) $(DIR_LIB) $(LIBS)
clean:
-rm -f *.o core *.out *.log
-rm -f $(APP)
sync
install : $(APP)
-cp $(APP) $(INSTALL_BIN)/$(APP)
sync
# End of Makefile
+322
View File
@@ -0,0 +1,322 @@
#include "ProcessConfig.h"
#include <stdlib.h>
#include "Logger.h"
// static 변수 초기화
CProcessConfig * CProcessConfig::m_pInstance = NULL;
// conf 상의 FHS_LISTEN_PORT_FOR_STATUS_MONITORING 정보가 없을 경우 사용할 Default 정보
// - GMS, Neocast 등에서 FHS 상태를 감시하기 위한 TCP port
// - 본 모듈에서 해당 TCP port 를 Listen 하면서 서비스 상태에 따라 TCP 상태를 제어하여
// - FHS 서비스 불가 등의 문제 발생시.. GMS, Neocast 가 이를 인지할 수 있도록 하기 위한 Port 정보임
#define DEFAULT_TCP_LISTEN_PORT_FOR_STATUS_MONITORING 8083
// 생성자
CProcessConfig::CProcessConfig()
{
// default 값 설정.
// FHS_LISTEN_PORT_FOR_STATUS_MONITORING
m_nListenPortForStatusMonitoring = DEFAULT_TCP_LISTEN_PORT_FOR_STATUS_MONITORING;
}
// 소멸자
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, "RCID", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcId = strValue;
strValue.clear();
}
// FILE_STORAGE_ROOT
if( GetConfigValue( conf, strProgramName, "FILE_STORAGE_ROOT", strValue, strErrorMessage ) == false )
return false;
else
{
m_strStorageRoot = strValue;
strValue.clear();
}
// RCTS_SERVER
if( GetConfigValue( conf, strProgramName, "RCTS_SERVER", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRctsServer = strValue;
strValue.clear();
}
// FHS_PORT_MNGCHECK
// - fhs fimngd 의 alive check 를 위한 tcp port
if( GetConfigValue( conf, strProgramName, "FHS_PORT_MNGCHECK", strValue, strErrorMessage ) == false )
return false;
else
{
m_nFimngdCheckPort = atoi( strValue.c_str() );
if( m_nFimngdCheckPort <= 0 || m_nFimngdCheckPort > 65535 )
{
strErrorMessage = "Config [FHS_PORT_MNGCHECK] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// FHS_LISTEN_PORT_FOR_STATUS_MONITORING
// - GMS, Neocast 등에서 FHS 상태를 감시하기 위한 TCP port
// - 본 모듈에서 해당 TCP port 를 Listen 하면서 서비스 상태에 따라 TCP 상태를 제어하여
// - FHS 서비스 불가 등의 문제 발생시.. GMS, Neocast 가 이를 인지할 수 있도록 하기 위한 Port 정보임
if( GetConfigValue( conf, strProgramName, "FHS_LISTEN_PORT_FOR_STATUS_MONITORING", strValue, strErrorMessage ) == false )
{
// 해당 항목이 존재하지 않을 경우
// 하위 호환성 및 binary 만 우선 적용 등의 이슈로 인해
// default 값을 적용 처리한다.
m_nListenPortForStatusMonitoring = DEFAULT_TCP_LISTEN_PORT_FOR_STATUS_MONITORING;
}
else
{
// 해당 값이 존재하는 경우
m_nListenPortForStatusMonitoring = atoi( strValue.c_str() );
if( m_nListenPortForStatusMonitoring <= 0 || m_nListenPortForStatusMonitoring > 65535 )
{
strErrorMessage = "Config [FHS_LISTEN_PORT_FOR_STATUS_MONITORING] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// SERVICE_NIC_NAME
// - 다중 값으로 정의 될 수 있으며.. 존재하지 않는 경우.. 오류로 처리
if( conf.GetConfig( strProgramName, "SERVICE_NIC_NAME", vecValue ) == true )
{
// 해당 값이 존재할 경우
// - GetConfig() 에서 결과값이 empty 인 경우.. false 를 반환하므로...
// - ture 반환시에는 정상적인 값이 존재한다고 판단해도 무방
m_vecSvcNic = vecValue;
vecValue.clear();
}
else
{
// program section 에 존재하지 않을 경우... COMMON 세션 확인
if( conf.GetConfig( "COMMON", "SERVICE_NIC_NAME", vecValue ) == true )
{
m_vecSvcNic = vecValue;
vecValue.clear();
}
else
{
// program, COMMON section 에 해당값이 모두 설정되지 않은 경우....
// 오류 처리
strErrorMessage = "Config [SERVICE_NIC_NAME] value is not valid";
return false;
}
}
// CHECK_DNS_LIST
// - 다중 값으로 정의 될 수 있으며..
// - 설정값이 존재하지 않을 수 있다.
if( conf.GetConfig( strProgramName, "CHECK_DNS_LIST", vecValue ) == true )
{
// 해당 값이 존재할 경우
// - GetConfig() 에서 결과값이 empty 인 경우.. false 를 반환하므로...
// - ture 반환시에는 정상적인 값이 존재한다고 판단해도 무방
m_vecCheckDns = vecValue;
vecValue.clear();
}
else
{
// program section 에 존재하지 않을 경우... COMMON 세션 확인
if( conf.GetConfig( "COMMON", "CHECK_DNS_LIST", vecValue ) == true )
{
m_vecCheckDns = vecValue;
vecValue.clear();
}
else
{
// program, COMMON section 에 해당값이 모두 설정되지 않은 경우....
// - 설정값이 존재하지 않을 수 있으므로.. 그냥 통과.
m_vecCheckDns.clear();
vecValue.clear();
}
}
// RCTS_PORT_INFO_SYSTEM
// - rcts rc_mond 의 system info 수신을 위한 TCP listen port 정보
if( GetConfigValue( conf, strProgramName, "RCTS_PORT_INFO_SYSTEM", strValue, strErrorMessage ) == false )
return false;
else
{
m_nRctsSystemInfoPort = atoi( strValue.c_str() );
if( m_nRctsSystemInfoPort <= 0 || m_nRctsSystemInfoPort > 65535 )
{
strErrorMessage = "Config [RCTS_PORT_INFO_SYSTEM] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// RCTS_PORT_INFO_SERVICE
// - rcts rc_mond 의 service check info 수신을 위한 TCP listen port 정보
if( GetConfigValue( conf, strProgramName, "RCTS_PORT_INFO_SERVICE", strValue, strErrorMessage ) == false )
return false;
else
{
m_nRctsServiceInfoPort = atoi( strValue.c_str() );
if( m_nRctsServiceInfoPort <= 0 || m_nRctsServiceInfoPort > 65535 )
{
strErrorMessage = "Config [RCTS_PORT_INFO_SERVICE] 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;
}
+130
View File
@@ -0,0 +1,130 @@
/****************************************************************************
csagentd config processing module
-----------------------------------------
begin : 2015/04/20
copyright : (C) 2005 Solbox Inc.
author : Dev Storage team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __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_strStorageRoot; // content save path
std::string m_strRctsServer; // RCTS ip or domain
unsigned int m_nFimngdCheckPort; // FHS fimngd alive check tcp port
// GMS, Neocast 등에서 FHS 상태를 감시하기 위한 TCP port
// 본 모듈에서 해당 TCP port 를 Listen 하면서 서비스 상태에 따라 TCP 상태를 제어하여
// FHS 서비스 불가 등의 문제 발생시.. GMS, Neocast 가 이를 인지할 수 있도록 하기 위한 Port 정보임
unsigned int m_nListenPortForStatusMonitoring;
// 프로세스 설정 정보
std::vector<std::string> m_vecSvcNic; // Service NIC name 정보 저장.
std::vector<std::string> m_vecCheckDns; // DNS resolve check 대상 dns name
// - 설정값이 존재하지 않을 수 있음.
unsigned int m_nRctsSystemInfoPort; // RCTS system info listen tcp port
unsigned int m_nRctsServiceInfoPort; // RCTS service info listen tcp port
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 * GetStorageRoot() { return m_strStorageRoot.c_str(); }
const char * GetRctsServer() { return m_strRctsServer.c_str(); }
std::vector<std::string> GetSvcNic() { return m_vecSvcNic; }
std::vector<std::string> GetCheckDns() { return m_vecCheckDns; }
unsigned int GetRctsSystemInfoPort() { return m_nRctsSystemInfoPort; }
unsigned int GetRctsServiceInfoPort() { return m_nRctsServiceInfoPort; }
unsigned int GetFimngdCheckPort() { return m_nFimngdCheckPort; }
unsigned int GetListenPortForStatusMonitoring() { return m_nListenPortForStatusMonitoring; }
};
#endif /* __PROCESS_CONFIG_H__ */
+128
View File
@@ -0,0 +1,128 @@
/****************************************************************************
socket 통신 관련 protocol 정의
-----------------------------------------
begin : 2015/05/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 __PROTOCOL_DEFINE_H__
#define __PROTOCOL_DEFINE_H__
#include "ShmDefine.h"
// Packet Header stx 코드
#define HEADER_STX_CODE 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)
// RCTS rc_mond 와 통신시 사용할 통신 Header
// - 계산상 크기는 16Byte 이지만...
// - 통신상 sizeof() : 20Byte ( 64Bit OS )
struct PacketHeader {
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 RC_ID_MAX_LENGTH 31
// RCTS rc_mond 로 전송할 system 수집 data 구조체 protocol version.
// - 최초 버전이므로 0x00 으로 설정.
#define PROTOCOL_VERSION_SYSTEM 0x00
// RCTS rc_mond 로 전송할 system 수집정보 data 구조체
// - 전송할 항목이 많아... struct 형식으로 전송 처리.
// - protocol version : 0x00
// - 공유메모리 SystemInfo 구조체를 사용하도록 처리할 수 있지만...
// -- 공유메모리 구조 변경시.. 통신 오류 발생 가능..
// -- 따라서 복잡하더라고.. 공유메모리 구조체와 통신 구조체를 별도로 분리 처리함.
struct PacketDataSystem {
char rcid[RC_ID_MAX_LENGTH+1]; // RCID 정보, 최대 31 자까지만 허용.
char hostname[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
};
// RCTS rc_mond 로 전송할 service 상태 data 구조체 protocol version.
// - 최초 버전이므로 0x00 으로 설정.
#define PROTOCOL_VERSION_SERVICE 0x00
// RCTS rc_mond 로 전송할 service 상태 data 구조체
// - protocol version : 0x00
struct PacketDataService {
char rcid[RC_ID_MAX_LENGTH+1]; // RCID 정보, 최대 31 자까지만 허용.
char hostname[HOST_NAME_MAX_LENGTH+1]; // hostname, key 값으로 사용, 최대 63자까지만 허용.
int service_stat; // Service 상태 정보, ShmDefine.h 정의 참고.
int storage_max_usage; // Storage 최대 사용률(%), ShmDefine.h 정의 참고.
};
#endif /* __PROTOCOL_DEFINE_H__ */
+208
View File
@@ -0,0 +1,208 @@
#include "RctsSocket.h"
#include "Logger.h"
#include <string.h>
#include <netinet/in.h>
// 생성자
CRctsSocket::CRctsSocket()
: CBaseSocket( SOCKET_NOT_VALID )
, m_iPacketHeaderLen( sizeof( struct PacketHeader ) )
, m_uPacketDataLen( 0 )
{
}
// 소멸자
CRctsSocket::~CRctsSocket()
{
// 명시적인 Close 처리.
Close();
}
// 객체 초기화
// - false : 전달된 인자값 오류시
bool CRctsSocket::Init( const char * szServer, unsigned int uPort, const char * szRcId )
{
// 전달된 변수의 유효성 여부 판단.
if( szServer == NULL )
{
LOG( LERR, "server name or ip is null." );
return false;
}
if( szRcId == NULL )
{
LOG( LERR, "rcid is null." );
return false;
}
if( strlen( szRcId ) > RC_ID_MAX_LENGTH )
{
LOG( LERR, "rcid[%s] too long. max length [%d]", szRcId, RC_ID_MAX_LENGTH );
return false;
}
// 멤버 변수에 저장 처리.
m_strServer = szServer;
m_uPort = uPort;
m_strRcId = szRcId;
return true;
}
// RCTS 로 수집된 시스템 정보를 전송한다.
bool CRctsSocket::SendSystemInfo( const struct SystemInfo * pSystem )
{
// Socket 이 유효, 연결되어 있는지 확인.
if( IsValidSocket() == false )
{
// RCTS 에 연결되지 않은 경우....
// - RCTS 와 연결 시도.
if( Connect( m_strServer, m_uPort ) == false )
{
LOG( LERR, "rcts connect fail. rcts[%s][%u]", m_strServer.c_str(), m_uPort );
Close();
return false;
}
}
// 각 Packet 전송 관련 구조체 초기화.
memset( &m_packetHeader, 0x00, sizeof( struct PacketHeader ) );
memset( &m_packetDataSystem, 0x00, sizeof( struct PacketDataSystem ) );
// system 수집 정보 전송을 위한 Packet Data 구조체 설정.
strcpy( m_packetDataSystem.rcid, m_strRcId.c_str() );
// 공유메모리 구조체를 그대로 복사해도 되지만...
// - 향후 항목 추가시 통신 오류 발생 가능.
// - 이로 인해 protocol version 기능이 추가되어 있음.
// - 따라서 통신 오류 등을 방지하기 위해 공유메모리, 통신 구조체가 별도 분리되어 있음.
strcpy( m_packetDataSystem.hostname, pSystem->hostname );
m_packetDataSystem.load_avg[0] = pSystem->load_avg[0];
m_packetDataSystem.load_avg[1] = pSystem->load_avg[1];
m_packetDataSystem.load_avg[2] = pSystem->load_avg[2];
m_packetDataSystem.memory_total = pSystem->memory_total;
m_packetDataSystem.memory_free = pSystem->memory_free;
m_packetDataSystem.memory_usage = pSystem->memory_usage;
m_packetDataSystem.swap_total = pSystem->swap_total;
m_packetDataSystem.swap_used = pSystem->swap_used;
m_packetDataSystem.swap_usage = pSystem->swap_usage;
m_packetDataSystem.storage_total = pSystem->storage_total;
m_packetDataSystem.storage_avail = pSystem->storage_avail;
m_packetDataSystem.storage_max_usage = pSystem->storage_max_usage;
m_packetDataSystem.disk_max_queue = pSystem->disk_max_queue;
m_packetDataSystem.disk_max_busy = pSystem->disk_max_busy;
m_packetDataSystem.network_packet_in = pSystem->network_packet_in;
m_packetDataSystem.network_packet_out = pSystem->network_packet_out;
m_packetDataSystem.network_traffic_in = pSystem->network_traffic_in;
m_packetDataSystem.network_traffic_out = pSystem->network_traffic_out;
m_packetDataSystem.tcp_total = pSystem->tcp_total;
m_packetDataSystem.tcp_80 = pSystem->tcp_80;
m_packetDataSystem.tcp_80_establish = pSystem->tcp_80_establish;
m_uPacketDataLen = sizeof( m_packetDataSystem );
// system 수집 정보 전송을 위한 Packet Header 설정.
m_packetHeader.stx = HEADER_STX_CODE;
m_packetHeader.type = HEADER_TYPE_REQUEST;
m_packetHeader.command[0] = COMMAND_TRANSFER_SYSTEM;
m_packetHeader.data_length = htonl( m_uPacketDataLen );
m_packetHeader.protocol_version = PROTOCOL_VERSION_SYSTEM;
// Packet Header 정보 전송
if( WriteN( &m_packetHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
{
LOG( LERR, "system info send fail to rc_mond. " );
// send 실패시... socket close 하여 다음 전송시 새로 생성되도록 한다.
Close();
return false;
}
else
{
// Data 부분 전송
WriteN( &m_packetDataSystem, m_uPacketDataLen );
}
_LOG( LINF, "[RCTS send system] body[%u] mem_usage[%d] storage_avail[%ju] traffic_out[%ju] tcp80_est[%d]"
, m_uPacketDataLen
, m_packetDataSystem.memory_usage
, m_packetDataSystem.storage_avail
, m_packetDataSystem.network_traffic_out
, m_packetDataSystem.tcp_80_establish );
return true;
}
// RCTS 로 서비스 상태 정보를 전송한다.
bool CRctsSocket::SendServiceStatus( std::string & strHostname, const struct ServiceStatus * pService, int & iStorageMaxUsage )
{
// Socket 이 유효, 연결되어 있는지 확인.
if( IsValidSocket() == false )
{
// RCTS 에 연결되지 않은 경우....
// - RCTS 와 연결 시도.
if( Connect( m_strServer, m_uPort ) == false )
{
LOG( LERR, "rcts connect fail. rcts[%s][%u]", m_strServer.c_str(), m_uPort );
Close();
return false;
}
}
// 각 Packet 전송 관련 구조체 초기화.
memset( &m_packetHeader, 0x00, sizeof( struct PacketHeader ) );
memset( &m_packetDataService, 0x00, sizeof( struct PacketDataService ) );
// 서비스 상태 정보 전송을 위한 Packet Data 구조체 설정.
strcpy( m_packetDataService.rcid, m_strRcId.c_str() );
strcpy( m_packetDataService.hostname, strHostname.c_str() );
m_packetDataService.service_stat = pService->stat;
m_packetDataService.storage_max_usage = iStorageMaxUsage;
m_uPacketDataLen = sizeof( m_packetDataService );
// 서비스 상태 정보 전송을 위한 Packet Header 설정.
m_packetHeader.stx = HEADER_STX_CODE;
m_packetHeader.type = HEADER_TYPE_REQUEST;
m_packetHeader.command[0] = COMMAND_TRANSFER_SERVICE;
m_packetHeader.data_length = htonl( m_uPacketDataLen );
m_packetHeader.protocol_version = PROTOCOL_VERSION_SERVICE;
// Packet Header 정보 전송
if( WriteN( &m_packetHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
{
LOG( LERR, "service status send fail to rc_mond. " );
// send 실패시... socket close 하여 다음 전송시 새로 생성되도록 한다.
Close();
return false;
}
else
{
// Data 부분 전송
WriteN( &m_packetDataService, m_uPacketDataLen );
}
_LOG( LINF, "[RCTS send service] hostname[%s] service stat[%d] max_usage[%d]"
, m_packetDataService.hostname
, m_packetDataService.service_stat
, m_packetDataService.storage_max_usage );
return true;
}
+83
View File
@@ -0,0 +1,83 @@
/****************************************************************************
RCTS socket class
-----------------------------------------
begin : 2015/05/14
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 __RCTS_SOCKET_H__
#define __RCTS_SOCKET_H__
#include "BaseSocket.h"
#include "ShmDefine.h"
#include "ProtocolDefine.h"
#include <unistd.h>
#include <string>
// CRctsSocket
// 본 클래스는 RCTS 와의 TCP socket 통신을 관리하고....
// Data 를 RCTS 로 전송하는 역활을 수행한다.
class CRctsSocket: public CBaseSocket
{
public:
// 생성자
// - 객체 생성시.. 접속 대상 RCTS 정보 및 통신시 사용할 RC ID 정보를 전달한다.
CRctsSocket();
// 소멸자
~CRctsSocket();
// 객체 초기화
// - false : 전달된 인자값 오류시
bool Init( const char * szServer, unsigned int uPort, const char * szRcId );
// RCTS 로 수집된 시스템 정보를 전송한다.
bool SendSystemInfo( const struct SystemInfo * pSystem );
// RCTS 로 서비스 상태 정보를 전송한다.
bool SendServiceStatus( std::string & strHostname, const struct ServiceStatus * pService, int & iStorageMaxUsage );
private:
// RCTS Server 정보
std::string m_strServer;
unsigned int m_uPort;
// 통신 관련 RC ID 정보
std::string m_strRcId;
// packet header 멤버 변수.
struct PacketHeader m_packetHeader;
// packet header 크기를 저장하기 위한 상수
const int m_iPacketHeaderLen;
// system 수집 정보 전송을 위한 data 구조체
struct PacketDataSystem m_packetDataSystem;
// service 상태 정보 전송을 위한 data 구조체
struct PacketDataService m_packetDataService;
// 전송할 Data 크기 정보를 저장할 멤버변수.
unsigned int m_uPacketDataLen;
};
#endif /* __RCTS_SOCKET_H__ */
File diff suppressed because it is too large Load Diff
+171
View File
@@ -0,0 +1,171 @@
/****************************************************************************
Service status check & shared memory set class
-----------------------------------------
begin : 2015/05/06
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __SERVICE_CHECK_H__
#define __SERVICE_CHECK_H__
#include "FhsShm.h"
#include "StorageInfo.h"
#include <string>
#include <vector>
// NIC 에 할당된 IP Type (AF_INET or AF_INET6) , IP 주소 정보를 저장하기 위한 구조체.
struct IpAddressInfo {
int type; // AF_INET or AF_INET6
std::string ip; // ip address
};
// ZFS 상태 정보 열거형 변수
typedef enum TypeZfsStatus {
ZFS_ERROR = -1, // Storage 상태 정보 수집 실패, FAULTED, OFFLINE, UNAVAIL 상태인 경우 -> Service Disable 처리 필요
ZFS_ONLINE, // 정상 상태 또는 ZFS 아닌 경우
ZFS_READONLY // DEGRADED 상태 -> Read only 처리 필요
} ZfsStatus;
// Service 상태 정보를 Check 하고... 그 결과를 공유메모리에 저장처리 하기 위한 class
class CServiceCheck
{
public:
// 생성자
CServiceCheck( CFhsShm * pFhsShm );
// 소멸자
~CServiceCheck();
// 초기화 함수
// - 초기화 작업 실패시 false 반환함.
bool Init( std::vector<std::string> vecNic, std::vector<std::string> vecDns, unsigned int uFimngdPort );
// Service 상태 Check 시작..
// - 오류 발생시.. false 반환.
bool Check();
private: // Service Check 관련 멤버 변수
// 공유메모리 interface 객체에 대한 포인터.
// - 공유메모리에 Data Set, Get 을 담당
CFhsShm * m_pFhsShm;
// Service NIC name 정보 저장.
std::vector<std::string> m_vecServiceNic;
// DNS resolve check 대상 dns name
// - 설정값이 존재하지 않을 수 있음
std::vector<std::string> m_vecCheckDns;
// fimngd check 관련 port 번호
unsigned int m_uCheckFimngdPort;
// fimngd 접속 실패 관련 count 정보를 저장하기 위한 변수.
// - 최초 기동시 -1 로 설정 처리.
int m_iFimngdDeadCount;
// Service NIC 에 할당된 IP 정보를 저장하기 위한 변수.
std::vector<struct IpAddressInfo> m_vecServiceIp;
// Storage 의 ZFS 여부 정보를 추출, 판단 처리할 Stroage Info 객체
CStorageInfo m_storageInfo;
struct timespec m_timeStart; // service check 소요시간 측정을 위한 멤버변수
struct timespec m_timeEnd; // service check 소요시간 측정을 위한 멤버변수
double m_dElapsedTime; // service check 소요시간 측정을 위한 멤버변수
private: // Check 관련 내부 함수
// fsck 프로세스 동작 여부 체크.
// - true : 해당 프로세스 동작 중
// - false : 동작 안함.
bool IsFsckRun();
// 멤버 변수 m_vecSvcNic 에 저장된 서비스 NIC 에 대한 상태 체크 수행
// - true : 모두 정상
// - false : 오류 발생 또는 1개라도 active 상태가 아닌 경우.
bool IsAliveServiceNic();
// 멤버 변수 m_vecSvcNic 에 저장된 서비스 NIC 에 대한 IP 정보를 추출하여 멤버 변수 m_vecServiceIp 에 저장
// - true : 추출 성공시.
// - false : 추출된 service IP 정보가 존재하지 않는 경우. ( 오류 발생 등으로 인해 추출 불가시 포함)
bool GetServiceIp();
// Apache 의 80 port 에 접속 가능한지 check.
// return true : 정상
// false : 추출된 서비스 IP 로 접속 테스트 수행시.. 1개라도 접속이 실패한 경우(Timeout 발생 포함)
// - 2015-05-11 현재 opendav 가 80 port listen 처리시 IPv4 에 대해서만 Listen 처리하므로...
// - 추출된 서비스 IP 중 IPv4 에 대해서만 테스트를 수행한다.
bool IsAliveApache();
// DNS resolve 체크 수행.
// - 장비의 hostname 을 기준으로 m_vecCheckDns 에 정의된 DNS 서버에 resolve 질의
// - resolve 결과가.. m_vecServiceIp 에 존재하는 경우.. true.
// - 만약 resolve 결과가 m_vecServiceIp 에 존재하지 않는 경우...
// -- 해당 domain 이 다른 장비를 가르키는 것일 수 있으므로.. fasle 반환.
bool IsResolveDns();
// Default DNS resolve check
// - 장비의 hostname 을 기준으로 시스템에 설정된 기본 DNS 서버에 resolve 질의
// - resolve 결과가.. m_vecServiceIp 에 존재하는 경우.. true.
// - 위의 DNS check 관련 설정 정보가 없거나.. Alive DNS 가 없는 경우 처리를 수행하기 위한 보조 방법임
bool IsResolveDefaultDns();
// std 상에 trim 함수가 없어서 직접 구현. 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
// @return void
void Trim( std::string & str );
// FHS 의 fimngd 프로세스 동작 여부 check.
// - init() 에서 설정된 m_uCheckFimngdPort 정보와 추출된 서비스 IP 를 이용하여 TCP 접속 테스트 수행.
// @return true 접속 성공시
// false 접속 실패 또는 오류 발생시.
bool IsAliveFimngd();
// Storage Write/Read 테스트 결과가 정상인지 Check
// - Storage 저장 공간에 대한 file write / read 테스트가 worker 프로세스에 의해 주기적으로 수행
// - 만약 test 실패 발생시 공유 메모리 상의 관련 값이 -1 로 설정됨
// - 만약 test 중 hang 발생시 공유 메모리 상의 값을 확인하여 timeout 여부 판단 필요.
// @return true storage check 정상인 경우
// false storage check 실패 또는 timeout 상황인 경우
bool IsNormalStatusStorageCheck();
// System Collecotr 관련 Worker 프로세스가 정상 동작하는지 Check
// - system 상태 정보 수집을 위한 system collector worker 프로세스는 주기적으로 수행되며
// - 수집 시작시간, 수집된 정보를 공유메모리에 저장
// - 만약 여러 요인에 의해 해당 worker 프로세스가 hang 상황인 경우 서비스 상태를 disable 로 변경 처리해야 함
// - 이를 판단하기 위한 함수로 공유메모리 정보를 이용하여 timeout 판단함.
// @return true 정상인 경우
// false timeout 상황인 경우
bool IsNormalStatusSystemCollect();
// ZFS 상태 관련 정보 추출하여 정상인지 Check
ZfsStatus GetZfsStatus();
};
#endif /* __SERVICE_CHECK_H__ */
+222
View File
@@ -0,0 +1,222 @@
/***************************************************************************
csagentd shared memory template header
-----------------------------------------
begin : 2015/04/13
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__ */
+145
View File
@@ -0,0 +1,145 @@
/****************************************************************************
Shared memory define for csagentd
-----------------------------------------
begin : 2015/04/20
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __SHM_DEFINE_H__
#define __SHM_DEFINE_H__
#include <unistd.h>
#include <sys/types.h>
// hostname 최대 길이.
#define HOST_NAME_MAX_LENGTH 63
// Service 상태 코드 정의
// TODO : 서비스 상태 코드 추가시 다음 작업 수행해야 함.
// - SERVICE_STAT_MAX 값을 최대 값으로 변경 처리.
// - CFhsShm 생성자에 화면 출력용 문자열 추가 및 변경 .
#define SERVICE_STAT_OK 0 // 정상 상태
#define SERVICE_STAT_OUT 1 // 수동 중지 상태
#define SERVICE_STAT_DISABLE 2 // 서비스 불가 상태
#define SERVICE_STAT_RONLY 3 // Read Only 상태
#define SERVICE_STAT_MAX 3 // Service 상태 코드 범위 Check 를 위한 값
// 독립 worker 프로세스가 storage 에 대한 write/read 테스트 수행하여 성공시 공유메모리에 시간 정보 기록
// 지정된 시간 동안 테스트를 성공하지 못하는 경우 Hang 으로 판단하여 서비스 disable 처리를 위한
// timeout 값 (단위 sec)
#define TIMEOUT_STORAGE_CHECK 30
// 독립 Worker 프로세스인 system collector 는 정보 수집 시작 시간을 공유메모리에 기록
// 만약 시스템 Hang 상황으로 system 관련 정보 추출 함수 block 발생시
// 이에 대한 timeout 을 판단하여 서비스 disable 상태로 변경하기 위한 timeout 값 (단위 sec)
#define TIMEOUT_SYSTEM_COLLECT 40
// 독립 Worker 프로세스인 service check 는 정보 수집 시작 시간을 공유메모리에 기록
// 만약 시스템 Hang 상황으로 service check 관련 함수 block 발생시
// 이에 대한 timeout 을 판단하여 서비스 disable 상태로 변경하기 위한 timeout 값 (단위 sec)
#define TIMEOUT_SERVICE_CHECK 15
// 2018-02-07 NEW huibong disk busy 상황시 Read Only 상태로 변경 처리를 위한 기준값 선언 (#31701)
// - Disk Busy 상황시 응답 지연 발생 또는 장비 Hang 상황인 것으로 판단됨
// - 2018-01 SK 에서 발생한 응답 지연으로 인한 SolProxy 오류 확인 결과...
// - queue 가 7 이상 & disk busy 50 % 이상인 경우 응답 지연 발생됨
// - 따라서 이에 대한 기준값을 정의하고.. 해당 값 이상인 경우 Read Only 상태로 변경할 수 있도록 한다.
#define DISK_BUSY_MIN_QUEUE_COUNT 7 // disk max queue 가 해당 값 이상인 경우 Disk Busy 상황으로 판단
#define DISK_BUSY_MIN_BUSY_PERCENT 50.0 // disk maxy busy 값이 해당 값 이상인 경우 Disk Busy 상황으로 판단
#ifdef __cplusplus
extern "C" {
#endif
// System info
struct SystemInfo {
char hostname[HOST_NAME_MAX_LENGTH + 1]; // hostname, 최대 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/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
};
// Service status
struct ServiceStatus {
int stat; // Service stat code.
};
// Shared momory
struct FhsInfo {
// 2018-01-30 NEW huibong 마지막 system 정보 추출 시작 시간 (#31701)
// - 추출 관련 hang 상황 발생시 expire 처리를 위한 시간값
// - 시간 동기화 영향을 없애기 위해 clock_gettime(CLOCK_MONOTONIC) 을 통해 추출된 sec 정보임
time_t time_system_check_start_sec;
struct SystemInfo system;
// 2018-01-30 NEW huibong 마지막 service check 시작 시간 (#31701)
// - service check 관련 hang 상황 발생시 expire 처리를 위한 시간값
// - 시간 동기화 영향을 없애기 위해 clock_gettime(CLOCK_MONOTONIC) 을 통해 추출된 sec 정보임
time_t time_service_check_start_sec;
struct ServiceStatus service;
// 2018-01-30 NEW huibong 신규로 추가한 Storage check 관련 last check 성공 시간 (#31701)
// - storage check 실패, hang 상황 발생시 expire 처리를 위한 시간값
// - 시간 동기화 영향을 없애기 위해 clock_gettime(CLOCK_MONOTONIC) 을 통해 추출된 sec 정보임
// - 0 : 최초 공유메모리 초기화시
// - -1 : storage check 실패 발생시
// - 그외 : storage check 를 마지막으로 성공한 시간 sec 정보 -> storage check hang 발생 가능하므로 expire 처리 필요.
time_t time_storage_check_ok_sec;
};
#ifdef __cplusplus
}
#endif
#endif /* __SHM_DEFINE_H__ */
+264
View File
@@ -0,0 +1,264 @@
#include "StorageCheck.h"
#include "Logger.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
// Storage Write / Read test 를 수행할 파일명 정의
#define CHECK_FILE_NAME PROG_NAME"_check.txt"
// 생성자
CStorageCheck::CStorageCheck( CFhsShm * pFhsShm )
: m_pFhsShm( pFhsShm )
{
}
// 소멸자
CStorageCheck::~CStorageCheck()
{
}
// 초기화 함수
// - szRoot : Write/Read 테스트를 수행할 mount 최상위 경로 정보.
bool CStorageCheck::Init( const char * szRoot )
{
// 공유메모리 접근 포인터가 유효하지 않은 경우
if( m_pFhsShm == NULL )
return false;
// 최초 storage 정보 추출을 수행해서 문제 없는지 체크
if( m_storageInfo.Init( szRoot ) == false )
{
LOG( LERR, "[Storage] storage info class init failed." );
return false;
}
// 소요시간 측정 관련 변수 초기화
memset( &m_timeTotalStart, 0x00, sizeof( struct timespec ) );
memset( &m_timeTotalEnd, 0x00, sizeof( struct timespec ) );
m_dElapsedTotalTime = 0.0;
memset( &m_timeDiskStart, 0x00, sizeof( struct timespec ) );
memset( &m_timeDiskEnd, 0x00, sizeof( struct timespec ) );
m_dElapsedDiskTime = 0.0;
return true;
}
// Storage 상태 Check 시작
// - szRoot : Write/Read 테스트를 수행할 mount 최상위 경로 정보.
// - 오류 발생시 false 반환
// - Hang 발생 가능하므로 본 함수 호출부에서 timeout 처리 필요
bool CStorageCheck::Check( const char * szRoot )
{
// Storage info 객체 초기화
// - 전달받은 최상위 경로값 오류시
// - 정보 추출 실패 또는 szRoot 에 맞는 경로 정보 추출 실패시 false 반환
// - mount 정보가 중간에 변경될 수 있으므로.. Check 수행시마다.. Storage 정보 추출하도록 한다.
if( m_storageInfo.Init( szRoot ) == false )
{
LOG( LERR, "[Storage] storage info class init failed.");
return false;
}
// write, read check 대상 경로 저장을 위한 vector
// - 전달받은 root 경로 정보를 이용하여 테스트 수행할 경로 정보 추출 저장
std::vector< std::string > vecCheckPath;
if( m_storageInfo.GetStoragePath( vecCheckPath ) == false )
{
// storage 정보가 유효하지 않은 경우
LOG( LERR, "[Storage] storage info not valid.");
return false;
}
// 실제 추출된 경로에 대한 파일 write, read 테스트 수행
// 변수 선언 & 초기화
std::vector< std::string >::iterator it;
std::string szFullFileName;
FILE * pFile = NULL;
int errorNum;
char szErrorBuffer[1024];
char szDataBuffer[1024];
char szReadBuffer[1024];
// write, read, unlink 작업 전 잠시 대기를 위한 sleep 설정
struct timespec sleepInterval;
sleepInterval.tv_sec = 0;
sleepInterval.tv_nsec = 200000000; // 0.2 sec
// check start time
clock_gettime( CLOCK_MONOTONIC, &m_timeTotalStart );
// 파일 저장 Data 생성
snprintf( szDataBuffer, sizeof( szDataBuffer ) - 1, "%ld.%ld", m_timeTotalStart.tv_sec, m_timeTotalStart.tv_nsec );
_LOG( LINF, "[Storage] check start with data [%s]", szDataBuffer );
// 설정된 경로별 파일 write, read 테스트 수행
for( it = vecCheckPath.begin(); it < vecCheckPath.end(); it++ )
{
// check start time
clock_gettime( CLOCK_MONOTONIC, &m_timeDiskStart );
// 파일명 조립
szFullFileName = *it + "/" + CHECK_FILE_NAME;
// 파일 open
pFile = fopen( szFullFileName.c_str(), "w" );
if( pFile == NULL )
{
// 파일 open 실패시
errorNum = errno;
snprintf( szErrorBuffer, sizeof( szErrorBuffer ) - 1, "File open failed to write.[%s] [%d][%s]", szFullFileName.c_str(), errorNum, strerror( errorNum ) );
LOG( LERR, "[Storage] %s", szErrorBuffer );
return false;
}
// 파일 write
// - fwrite 가 지정된 크기만큼 write 실패한 경우.. write error 발생
if( fwrite( szDataBuffer, 1, strlen( szDataBuffer ), pFile ) != strlen( szDataBuffer ) )
{
// write 실패시
errorNum = errno;
snprintf( szErrorBuffer, sizeof( szErrorBuffer ) - 1, "File write failed.[%s] [%d][%s]", szFullFileName.c_str(), errorNum, strerror( errorNum ) );
fclose( pFile );
pFile = NULL;
LOG( LERR, "[Storage] %s", szErrorBuffer );
return false;
}
// buffer flush 처리 - I/O 오류 발생으로 실패 발생 가능
if( fflush( pFile ) != 0 )
{
// flush 처리 실패시
errorNum = errno;
snprintf( szErrorBuffer, sizeof( szErrorBuffer ) - 1, "File write flush failed.[%s] [%d][%s]", szFullFileName.c_str(), errorNum, strerror( errorNum ) );
fclose( pFile );
pFile = NULL;
LOG( LERR, "[Storage] %s", szErrorBuffer );
return false;
}
//_LOG( LINF, "[Storage] %s check file write & flush ok. [%s]", szFullFileName.c_str(), tempBuffer );
// 파일 close
if( pFile != NULL )
{
fclose( pFile );
pFile = NULL;
}
// 잠시 대기
nanosleep( &sleepInterval, NULL );
// 파일 open
pFile = fopen( szFullFileName.c_str(), "r" );
if( pFile == NULL )
{
// 파일 open 실패시
errorNum = errno;
snprintf( szErrorBuffer, sizeof( szErrorBuffer ) - 1, "File open failed to read.[%s] [%d][%s]", szFullFileName.c_str(), errorNum, strerror( errorNum ) );
LOG( LERR, "[Storage] %s", szErrorBuffer );
return false;
}
// 파일 read
// - 파일 읽은 크기가 실제 저장한 크기보다 작은 경우
// - end of file 인지 read error 인지 체크해야 하며 (man page)
// - read buffer 는 작업 전 초기화하지 않으면.. garbage data 가 저장되어 있어 문제됨.
memset( szReadBuffer, 0x00, sizeof( szReadBuffer ) );
if( fread( szReadBuffer, 1, sizeof( szReadBuffer ), pFile ) != strlen( szDataBuffer ) )
{
// read 실패시
// - end of file : 실제 저장한 data 와 크기가 안 맞으므로.. 이거는 오류로 본다
// - read 실패 : read 오류로 처리
if( feof( pFile ) != 0 ) // end of file 인 경우
{
snprintf( szErrorBuffer, sizeof( szErrorBuffer ) - 1, "File read size not match[%s] src[%s] file[%s]"
, szFullFileName.c_str(), szDataBuffer, szReadBuffer );
}
else
{
errorNum = errno;
snprintf( szErrorBuffer, sizeof( szErrorBuffer ) - 1, "File read failed.[%s] [%d][%s]", szFullFileName.c_str(), errorNum, strerror( errorNum ) );
}
fclose( pFile );
pFile = NULL;
LOG( LERR, "[Storage] %s", szErrorBuffer );
return false;
}
// read data check
if( strcmp( szDataBuffer, szReadBuffer ) != 0 )
{
snprintf( szErrorBuffer, sizeof( szErrorBuffer ) - 1, "File read data not match[%s]. src[%s] file[%s]"
, szFullFileName.c_str(), szDataBuffer, szReadBuffer );
fclose( pFile );
pFile = NULL;
LOG( LERR, "[Storage] %s", szErrorBuffer );
return false;
}
// 파일 close
if( pFile != NULL )
{
fclose( pFile );
pFile = NULL;
}
// 잠시 대기
nanosleep( &sleepInterval, NULL );
// 파일 unlink
if( unlink( szFullFileName.c_str() ) != 0 )
{
// unlink 실패시
errorNum = errno;
snprintf( szErrorBuffer, sizeof( szErrorBuffer ) - 1, "File unlink failed.[%s] [%d][%s]", szFullFileName.c_str(), errorNum, strerror( errorNum ) );
LOG( LERR, "[Storage] %s", szErrorBuffer );
return false;
}
// check end time
clock_gettime( CLOCK_MONOTONIC, &m_timeDiskEnd );
// disk 당 작업 소요 시간 계산
m_dElapsedDiskTime = m_timeDiskEnd.tv_sec - m_timeDiskStart.tv_sec;
m_dElapsedDiskTime += ( m_timeDiskEnd.tv_nsec - m_timeDiskStart.tv_nsec ) * 1e-9;
_LOG( LINF, "[Storage] %s check ok. elapsed time: %.3lf sec.", it->c_str(), m_dElapsedDiskTime );
}
// check end time
clock_gettime( CLOCK_MONOTONIC, &m_timeTotalEnd );
// 작업 소요 시간 계산
m_dElapsedTotalTime = m_timeTotalEnd.tv_sec - m_timeTotalStart.tv_sec;
m_dElapsedTotalTime += ( m_timeTotalEnd.tv_nsec - m_timeTotalStart.tv_nsec ) * 1e-9;
_LOG( LINF, "[Storage] check end. total elapsed time: %.3lf sec.", m_dElapsedTotalTime );
return true;
}
+74
View File
@@ -0,0 +1,74 @@
/****************************************************************************
Storage write / read check class
-----------------------------------------
begin : 2018/01/29
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 __STORAGE_CHECK_H__
#define __STORAGE_CHECK_H__
#include "FhsShm.h"
#include "StorageInfo.h"
#include <unistd.h>
#include <string>
#include <vector>
// Storage 관련 임시 file write, read 테스트를 수행하기 위한 class
class CStorageCheck
{
public:
// 생성자
CStorageCheck( CFhsShm * pFhsShm );
// 소멸자
~CStorageCheck();
public: // Storage Write, Read 테스트 관련 함수
// 초기화 함수
bool Init( const char * szRoot );
// Storage 상태 Check 시작
// - 인자값으로 최상위 storage root path 정보 입력 (check 시마다 정보 추출하여 check 수행하도록 하기 위함)
// - 오류 발생시 false 반환
// - Hang 발생 가능하므로 본 함수 호출부에서 timeout 처리 필요
bool Check( const char * szRoot );
private: // 멤버 변수
// 공유메모리 interface 객체에 대한 포인터.
// - 공유메모리에 Data Set, Get 을 담당
CFhsShm * m_pFhsShm;
// Storage 관련 정보 추출을 담당할 객체.
CStorageInfo m_storageInfo;
struct timespec m_timeTotalStart; // total 소요시간 측정을 위한 멤버변수
struct timespec m_timeTotalEnd; // total 소요시간 측정을 위한 멤버변수
double m_dElapsedTotalTime; // total 소요시간 측정을 위한 멤버변수
struct timespec m_timeDiskStart; // 각 disk 단위 소요시간 측정을 위한 멤버변수
struct timespec m_timeDiskEnd; // 각 disk 단위 소요시간 측정을 위한 멤버변수
double m_dElapsedDiskTime; // 각 disk 단위 소요시간 측정을 위한 멤버변수
private: // 처리 관련 내부 함수
};
#endif /* __STORAGE_CHECK_H__ */
+266
View File
@@ -0,0 +1,266 @@
#include "StorageInfo.h"
#include "Logger.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <math.h>
// 생성자
CStorageInfo::CStorageInfo()
{
// 멤버변수 초기화
m_vecStorageInfo.clear();
}
// 소멸자
CStorageInfo::~CStorageInfo()
{
m_vecStorageInfo.clear();
}
// 전달받은 경로로부터.. 상위 Path 정보를 추출하여 전달한다.
// 오류 발생시 [out] 값인 strUpperPath 가 empty 된다.
void CStorageInfo::GetUpperPath( const std::string & strSourcePath, std::string & strUpperPath )
{
// 인자로 전달되는 strSourcePath 끝에.. '/' 가 이미 제거되었다고 판단하고 작업을 수행한다.
std::string::size_type pos = strSourcePath.rfind( '/' );
if( pos == std::string::npos )
{
// 찾지 못한 경우...
// out 값을 empty 처리시킨다.
LOG( LNOT, "upper path not found. source[%s]", strSourcePath.c_str() );
strUpperPath.clear();
return;
}
// 찾은 경우..
strUpperPath = strSourcePath.substr( 0, pos );
// 추출된 경로가... 최상위 '/' 인 경우.. 유효하지 않으므로 오류로 처리한다.
if( strcmp( strUpperPath.c_str(), "/" ) == 0 )
{
LOG( LNOT, "upper path not vaild that root path. [%s]", strUpperPath.c_str() );
strUpperPath.clear();
}
return;
}
// 초기화 함수
// - szRoot : Content 저장 최상위 경로 정보.
bool CStorageInfo::Init( const char * szRoot )
{
// 전달받은 최상위 경로 정보에 대한 Check
if( szRoot == NULL || strlen( szRoot ) <= 0 )
{
LOG( LERR, "storage root path not valid.");
return false;
}
// 추출된 정보 저장을 위한 임시변수
int iMountCount = 0;
struct statfs * pMountBuf = NULL;
std::vector< struct stStorageInfo > vecStorageAll;
std::vector< struct stStorageInfo >::iterator it;
struct stStorageInfo stStorage;
// 추출 정보 저장 및 멤버 변수 초기화
vecStorageAll.clear();
m_vecStorageInfo.clear();
// Mount 정보 추출
// - MNT_WAIT flag 사용시 정확한 정보를 추출, 하지만 block 발생 가능
// - 따라서 항상 정확한 정보를 추출할 필요는 없으며.. block 이 발생하지 않도록
// - MNT_NOWAIT flag 사용한다.
iMountCount = getmntinfo( &pMountBuf, MNT_NOWAIT );
if( iMountCount == 0 )
{
// 정보 추출 실패시 오류 내역 로깅
int errorNum = errno;
LOG( LERR, "Storage info init error. getmntinfo() fail[%d]. [%d][%s]"
, iMountCount, errorNum, strerror( errorNum ) );
return false;
}
// 추출된 mount 관련 모든 정보를 임시 변수에 저장
for( int i = 0; i < iMountCount; i++ )
{
stStorage.strName = pMountBuf[i].f_mntonname;
stStorage.strFsTypeName = pMountBuf[i].f_fstypename;
stStorage.ulUsedByte = ( pMountBuf[i].f_blocks - pMountBuf[i].f_bfree ) * pMountBuf[i].f_bsize;
stStorage.ulAvailableByte = pMountBuf[i].f_bavail * pMountBuf[i].f_bsize;
stStorage.usage = 0;
vecStorageAll.push_back( stStorage );
}
// 전달받은 mount 최상위 경로와 동일한 명칭의 mount 가 존재하는 검사
// - 이럴 경우는 거의 없는데.. 혹시나 해서 기능 탑재함.
for( it = vecStorageAll.begin(); it < vecStorageAll.end(); it++ )
{
if( strcmp( szRoot, it->strName.c_str() ) == 0 )
{
m_vecStorageInfo.push_back( *it );
}
}
// 전달받은 최상위 mount 정보와 동일한 명칭의 mount 가 존재하지 않는 경우..
// - sub path 부분을 제외한 후 다시 장비 Mount 정보와 비교한다.
// - 장비 mount 정보를 찾을 때까지 본 비교를 반복하여 수행한다.
if( m_vecStorageInfo.empty() == true )
{
std::string strPreviousPath = szRoot;
std::string strCurrentPath = szRoot;
do
{
// 수정된 경로 정보만을 가지고 부분 비교를 수행....
for( it = vecStorageAll.begin(); it < vecStorageAll.end(); it++ )
{
// 부분 비교 처리
if( strncmp( strCurrentPath.c_str(), it->strName.c_str(), strlen( strCurrentPath.c_str() ) ) == 0 )
{
m_vecStorageInfo.push_back( *it );
}
}
// 위에서 정보 추출이 실패한 경우..상위 Path 정보를 추출한다. ( /user2/dav_storage, /stg/node0/aa/bb 등으로 설정된 경우를 위해 )
if( m_vecStorageInfo.empty() == true )
{
GetUpperPath( strPreviousPath, strCurrentPath );
if( strCurrentPath.empty() == true )
{
// 상위 path 가 더이상 유효하지 않은 경우.
break;
}
strPreviousPath = strCurrentPath;
}
} while( m_vecStorageInfo.empty() == true );
}
// 임시 vector 에 저장된 내용 초기화
vecStorageAll.clear();
// 최종
if( m_vecStorageInfo.empty() == true )
{
LOG( LERR, "Storage info init error. root path[%s] not found in system mount.", szRoot );
return false;
}
// 추출 성공시
// - Storage 사용률 정보 계산해서 저장해 놓는다.
for( it = m_vecStorageInfo.begin(); it < m_vecStorageInfo.end(); it++ )
{
if( it->ulAvailableByte + it->ulUsedByte == 0 )
it->usage = 100;
else
{
// 사용률은 정수로 구하며... 나머지가 존재할 경우... 올림 처리한다.
it->usage = ( int ) ceil( ( double ) it->ulUsedByte / ( it->ulUsedByte + it->ulAvailableByte ) * 100.0 );
}
}
return true;
}
// 멤버 vector 에 저장된 경로의 Total Size, Total availsize, Max usage 정보를 반환.
bool CStorageInfo::GetStorageSizeInfo( uint64_t & ulFhsTotal, uint64_t & ulFhsAvailable, int & iMaxUsage )
{
// storage 정보가 비어 있는 경우
if( m_vecStorageInfo.empty() == true )
return false;
// output 인자 초기화.
ulFhsTotal = 0;
ulFhsAvailable = 0;
iMaxUsage = 0;
// total : 각 mount 의 used + available size 합계.
// available : 각 mount 의 합계.
// 최대 storage 사용률 정보 추출
std::vector< struct stStorageInfo >::iterator it;
for( it = m_vecStorageInfo.begin(); it < m_vecStorageInfo.end(); it++ )
{
// 전체 용량, 사용 가능 용량 합산
ulFhsTotal = ulFhsTotal + it->ulUsedByte + it->ulAvailableByte;
ulFhsAvailable = ulFhsAvailable + it->ulAvailableByte;
// 최대 사용률 정보 추출
if( it->usage > iMaxUsage )
{
iMaxUsage = it->usage;
}
// 2018-02-06 NEW huibong Storage 사용률이 90% 초과하는 경우 해당 Storage 의 정보를 로깅 처리한다.(#31701)
if( it->usage > 90 )
{
_LOG( LINF, "[Storage Info] storage usage: %s %3d %%, used[%14ju], avail[%14ju]."
, it->strName.c_str(), it->usage, it->ulUsedByte, it->ulAvailableByte );
}
else
{
_LOG( LDBG, "%s used[%14ju] avail[%14ju] usage[%3d %%]"
, it->strName.c_str(), it->ulUsedByte, it->ulAvailableByte, it->usage );
}
}
return true;
}
// 멤버 vector 에 저장된 mount 경로명 정보를 반환
bool CStorageInfo::GetStoragePath( std::vector<std::string>& vecPath )
{
// storage 정보가 비어 있는 경우
if( m_vecStorageInfo.empty() == true )
return false;
// output 인자 초기화.
vecPath.clear();
std::vector< struct stStorageInfo >::iterator it;
for( it = m_vecStorageInfo.begin(); it < m_vecStorageInfo.end(); it++ )
{
vecPath.push_back( it->strName );
}
return true;
}
// 멤버 vector 에 저장된 storage 정보 중에 ZFS file system 정보 존재하는지 Check
bool CStorageInfo::IsExistZFS()
{
// storage 정보가 비어 있는 경우 - ZFS file system 없는 것으로 처리
if( m_vecStorageInfo.empty() == true )
return false;
std::vector< struct stStorageInfo >::iterator it;
for( it = m_vecStorageInfo.begin(); it < m_vecStorageInfo.end(); it++ )
{
if( strcasecmp( "zfs", it->strFsTypeName.c_str() ) == 0 )
{
// zfs filesystem 이 존재하는 경우...
// - 나머지는 check 할 필요 없이 그냥 true 반환.
return true;
}
}
// zfs filesystem 이 없는 경우
return false;
}
+77
View File
@@ -0,0 +1,77 @@
/****************************************************************************
Storage info class
-----------------------------------------
begin : 2018/02/06
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __STORAGE_INFO_H__
#define __STORAGE_INFO_H__
#include <unistd.h>
#include <string>
#include <vector>
// 각 Storage mount 의 정보를 저장할 구조체
struct stStorageInfo {
std::string strName; // mount name, statfs::f_mntonname 항목
std::string strFsTypeName; // zfs, ufs 형식의 Type Name, statfs::f_fstypename 항목
uint64_t ulUsedByte; // 사용 중인 용량 Byte
uint64_t ulAvailableByte; // 사용 가능한 용량 Byte
int usage; // Storage 사용률(%), ceil() 함수 사용으로 소수점 올림 처리되어 95.1 % -> 96 % 로 처리됨
};
// Storage 관련 정보 추출 담당 class
class CStorageInfo
{
public:
// 생성자
CStorageInfo();
// 소멸자
~CStorageInfo();
// 초기화 함수
bool Init( const char * szRoot );
public: // Storage info 관련 interface 함수
// 멤버 vector 에 저장된 경로의 Total Size, Total availsize, Max usage 정보를 반환.
bool GetStorageSizeInfo( uint64_t & ulFhsTotal, uint64_t & ulFhsAvailable, int & iMaxUsage );
// 멤버 vector 에 저장된 mount 경로명 정보를 반환
bool GetStoragePath( std::vector< std::string > & vecPath );
// 멤버 vector 에 저장된 storage 정보 중에 ZFS file system 정보 존재하는지 Check
bool IsExistZFS();
private: // 멤버 변수
// init() 에서 추출된 정보 저장을 위한 vector 멤버변수
std::vector< struct stStorageInfo > m_vecStorageInfo;
private: // 처리 관련 내부 함수
// 전달받은 경로로부터.. 상위 Path 정보를 추출하여 전달한다.
void GetUpperPath( const std::string & strSourcePath, std::string & strUpperPath );
};
#endif /* __STORAGE_INFO_H__ */
+775
View File
@@ -0,0 +1,775 @@
#include "SystemInfo.h"
#include "Logger.h"
#include <errno.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/param.h>
#include <stddef.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <stdint.h>
#include <math.h>
#include <fcntl.h>
#include <devstat.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_mib.h>
#include <time.h>
#include <stdlib.h>
#include <sys/socketvar.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/tcp_var.h>
#include <arpa/inet.h>
#include <netinet/tcp_fsm.h>
#include <vector>
// 생성자
CSystemInfo::CSystemInfo( CFhsShm * pFhsShm )
: m_pFhsShm( pFhsShm )
, m_pKvm(NULL)
{
memset( &m_stDiskGmesh, 0x00, sizeof( struct gmesh ) );
m_pDiskSnapshotPrev = NULL;
m_pDiskSnapshotNow = NULL;
memset( &m_timeDiskPrev, 0x00, sizeof( struct timespec ) );
memset( &m_timeDiskNow, 0x00, sizeof( struct timespec ) );
// 최초 수집시 계산 오류 회피을 위해 0 으로 초기화.
m_timeNetworkPrev.tv_sec = 0;
m_timeNetworkPrev.tv_nsec = 0;
m_ulNetworkPacketIn = 0;
m_ulNetworkPacketOut = 0;
m_ulNetworkByteIn = 0;
m_ulNetworkByteOut = 0;
}
// 소멸자
CSystemInfo::~CSystemInfo()
{
if( m_pKvm != NULL )
kvm_close( m_pKvm );
// Disk 상태 수집 관련 변수, 포인트 해제 처리
if( m_pDiskSnapshotPrev != NULL )
geom_stats_snapshot_free( m_pDiskSnapshotPrev );
if( m_pDiskSnapshotNow != NULL )
geom_stats_snapshot_free( m_pDiskSnapshotNow );
geom_deletetree( &m_stDiskGmesh );
}
// 초기화 함수
bool CSystemInfo::Init()
{
if( m_pFhsShm == NULL )
return false;
// Kernel data access library 객체 초기화.
m_pKvm = kvm_open( NULL, "/dev/null", NULL, O_RDONLY, PROG_NAME":CSystemInfo" );
if( m_pKvm == NULL )
{
LOG( LERR, "init fail. kvm_open() error.");
return false;
}
// Disk 상태 정보 수집을 위한 멤버 변수 초기화.
int iResult = 0;
iResult = geom_gettree( &m_stDiskGmesh );
if( iResult != 0 )
{
LOG( LERR, "init fail. geom_gettree() return error[%d]", iResult );
return false;
}
iResult = geom_stats_open();
if( iResult )
{
LOG( LERR, "init fail. geom_stats_open() return error[%d]", iResult );
return false;
}
m_pDiskSnapshotPrev = NULL;
m_pDiskSnapshotPrev = geom_stats_snapshot_get();
if( m_pDiskSnapshotPrev == NULL )
{
LOG( LERR, "init fail. geom_stats_snapshot_get() return NULL.");
return false;
}
geom_stats_snapshot_timestamp( m_pDiskSnapshotPrev, &m_timeDiskPrev );
return true;
}
// hostname 정보 수집
bool CSystemInfo::GetHostname()
{
char tempBuffer[256];
if( gethostname( tempBuffer, 255 ) == 0 )
{
// 정보 추출 성공시
// - shared memory 에 set 시도.
if( m_pFhsShm->SetHostname( tempBuffer ) == false )
{
// shared memory 에 저장 실패시.
// - 로그는 해당 모듈에서 처리하므로.. false 반환.
return false;
}
}
else
{
// hostname 정보 추출 실패시.
// - 호출한 프로세스 모듈에서 프로세스 종료 등의 후처리를 진행하므로....
// - false 반환 외에 따로 진행할 작업 없음.
int errorNum = errno;
LOG( LERR, "gethostname() fail. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// load average 정보 수집
bool CSystemInfo::GetLoadAverage()
{
// 변수 초기화 안할 경우.. 오류 발생
int iIndex = 0;
struct loadavg stLoadAvg;
size_t length = sizeof( stLoadAvg );
memset( &stLoadAvg, 0x00, sizeof( struct loadavg ) );
if( sysctlbyname( "vm.loadavg", &stLoadAvg, &length, NULL, 0 ) == -1 )
{
// 함수 호출 실패시
// - 오류 내역 로깅 후
// - shraed memory 상의 설정 정보를 0 으로 초기화.
int errorNum = errno;
LOG( LERR, "load average collect error.[%d][%s]", errorNum, strerror( errorNum ) );
for( iIndex = 0; iIndex < 3; iIndex++ )
{
m_pFhsShm->SetLoadaverage( iIndex, (double)0.0 );
}
return false;
}
else
{
// 추출 성공시.
for( iIndex = 0; iIndex < 3; iIndex++ )
{
m_pFhsShm->SetLoadaverage( iIndex, (double)stLoadAvg.ldavg[iIndex] / stLoadAvg.fscale );
}
}
return true;
}
// total memory 정보 수집
bool CSystemInfo::GetMemoryTotal()
{
// 변수 초기화 안할 경우.. 오류 발생
uint64_t ulValue = 0;
size_t length = sizeof( ulValue );
if( sysctlbyname( "hw.physmem", &ulValue, &length, NULL, 0 ) == -1 )
{
// 함수 호출 실패시
// - 오류 내역 로깅 후
// - shraed memory 상의 설정 정보를 0 으로 초기화.
int errorNum = errno;
LOG( LERR, "total memory collect error.[%d][%s]", errorNum, strerror( errorNum ) );
ulValue = 0;
m_pFhsShm->SetMemoryTotal( ulValue );
return false;
}
else
{
// 추출 성공시
m_pFhsShm->SetMemoryTotal( ulValue );
}
return true;
}
// free memory 정보 수집
// 2018-01-10 CHG huibong
// - available memory 사용량 계산 방식 변경 free -> free + inactive + cache (#31360)
bool CSystemInfo::GetMemoryFree()
{
// 변수 초기화 안할 경우.. 오류 발생
uint64_t ulTotal = 0;
uint64_t ulValue = 0;
size_t length = sizeof( ulValue );
// free memory 정보 추출
if( sysctlbyname( "vm.stats.vm.v_free_count", &ulValue, &length, NULL, 0 ) == -1 )
{
// 함수 호출 실패시
// - 오류 내역 로깅 후
// - shraed memory 상의 설정 정보를 0 으로 초기화.
int errorNum = errno;
LOG( LERR, "available memory[free] collect error.[%d][%s]", errorNum, strerror( errorNum ) );
ulValue = 0;
m_pFhsShm->SetMemoryFree( ulValue );
return false;
}
ulTotal = ulTotal + ulValue;
// 변수 초기화 안할 경우.. 오류 발생
ulValue = 0;
// inactive memory 정보 추출
if( sysctlbyname( "vm.stats.vm.v_inactive_count", &ulValue, &length, NULL, 0 ) == -1 )
{
// 함수 호출 실패시
// - 오류 내역 로깅 후
// - shraed memory 상의 설정 정보를 0 으로 초기화.
int errorNum = errno;
LOG( LERR, "available memory[inactive] collect error.[%d][%s]", errorNum, strerror( errorNum ) );
ulValue = 0;
m_pFhsShm->SetMemoryFree( ulValue );
return false;
}
ulTotal = ulTotal + ulValue;
// 변수 초기화 안할 경우.. 오류 발생
ulValue = 0;
// cached memory 정보 추출
if( sysctlbyname( "vm.stats.vm.v_cache_count", &ulValue, &length, NULL, 0 ) == -1 )
{
// 함수 호출 실패시
// - 오류 내역 로깅 후
// - shraed memory 상의 설정 정보를 0 으로 초기화.
int errorNum = errno;
LOG( LERR, "available memory[cached] collect error.[%d][%s]", errorNum, strerror( errorNum ) );
ulValue = 0;
m_pFhsShm->SetMemoryFree( ulValue );
return false;
}
ulTotal = ulTotal + ulValue;
// 추출 성공시
// - 추출된 값이 page 단위이므로.. page 크기를 곱한다. ( pgagsize = 4096 )
ulTotal = ulTotal * getpagesize();
m_pFhsShm->SetMemoryFree( ulTotal );
return true;
}
// swap 정보 수집
bool CSystemInfo::GetSwap()
{
struct kvm_swap stSwap[1];
if( kvm_getswapinfo( m_pKvm, stSwap, 1, 0 ) == -1 )
{
// swap 정보 추출 실패시.
// - 오류 내역 로깅 후
// - shraed memory 상의 설정 정보를 0 으로 초기화.
LOG( LERR, "swap collect error. kvm_getswapinfo().");
m_pFhsShm->SetSwapInfo( 0, 0 );
return false;
}
else
{
// 추출 성공시
// - 추출된 값이 page 단위이므로.. page 크기를 곱한다. ( pgagsize = 4096 )
uint64_t ulTotal = 0;
uint64_t ulUsed = 0;
// swap total
ulTotal = (uint64_t)( stSwap[0].ksw_total ) * getpagesize();
// swap used
ulUsed = (uint64_t)( stSwap[0].ksw_used ) * getpagesize();
// shared memory set
m_pFhsShm->SetSwapInfo( ulTotal, ulUsed );
}
return true;
}
// mount 정보 수집 및 처리
// - szRoot : 정보를 수집할 mount 최상위 경로 정보.
bool CSystemInfo::GetMount( const char * szRoot )
{
// Storage 정보 추출 객체 초기화 실패시
if( m_storageInfo.Init( szRoot ) == false )
{
// 오류 내역 로깅 후
// shraed memory 상의 설정 정보를 0 으로 초기화.
LOG( LERR, "storage mount info class init failed." );
m_pFhsShm->SetStorageInfo( 0, 0, 0);
return false;
}
// Storage 관련 Total Size, Total Avvailable Size, Max usage percent 정보 추출
uint64_t ulFhsTotal = 0;
uint64_t ulFhsAvailable = 0;
int fhsMaxUsage = 0;
if( m_storageInfo.GetStorageSizeInfo( ulFhsTotal, ulFhsAvailable, fhsMaxUsage ) == false )
{
// 정보 추출 실패시
// - 오류 내역 로깅 후
// - shraed memory 상의 설정 정보를 0 으로 초기화.
LOG( LERR, "mount storage info collect fail. [%s] not found.", szRoot );
m_pFhsShm->SetStorageInfo( 0, 0, 0 );
return false;
}
else
{
// 정상 추출한 경우
// - 공유메모리 정보 update
m_pFhsShm->SetStorageInfo( ulFhsTotal, ulFhsAvailable, fhsMaxUsage );
}
return true;
}
bool CSystemInfo::GetDiskStatus()
{
uint64_t ulQueueLength = 0;
// FreeBSD 64bit 에서
// - double : 8 Byte
// - long double : 16 Byte
// - 본 변수 type 을 double type 으로 할 경우 오류 발생함.
// - 하지만. long double -> double 형변환은 유효 자리수 처리 문제를 제외하면.. 정상.
long double dBusy = 0.0;
uint64_t ulMaxQueueLength = 0;
double dMaxBusy = 0.0;
double dTimeInterval; // dt
struct gident * pGident; // gid
struct gprovider * pProvider; // pp
// 현재 snapshot 추출.
m_pDiskSnapshotNow = geom_stats_snapshot_get();
if( m_pDiskSnapshotNow == NULL )
{
// 오류 내역 로깅 후
// shraed memory 상의 설정 정보를 0 으로 초기화.
LOG( LERR, "now geom_stats_snapshot_get() return NULL." );
m_pFhsShm->SetDiskInfo( 0, (double)0.0 );
return false;
}
// 현재 시간 정보 추출.
geom_stats_snapshot_timestamp( m_pDiskSnapshotNow, &m_timeDiskNow );
// 이전 추출 시간과 현재 시간 간의 시간 간격 계산.
dTimeInterval = m_timeDiskNow.tv_sec - m_timeDiskPrev.tv_sec;
dTimeInterval += ( m_timeDiskNow.tv_nsec - m_timeDiskPrev.tv_nsec ) * 1e-9;
// 이전 수집 시간을 마지막 수집 시간으로 변경
m_timeDiskPrev = m_timeDiskNow;
// 수집된 snapshot 이 포인터 위치를 첫번째 인자로 reset 처리.
geom_stats_snapshot_reset( m_pDiskSnapshotPrev );
geom_stats_snapshot_reset( m_pDiskSnapshotNow );
struct devstat * pDevStatPrev; // gsq
struct devstat * pDevStatNow; // gsp
// 루프를 돌며서.. 필요 정보 추출
for( ;; )
{
// Disk stat 정보를 1개씩 추출
pDevStatNow = geom_stats_snapshot_next( m_pDiskSnapshotNow );
pDevStatPrev = geom_stats_snapshot_next( m_pDiskSnapshotPrev );
if( pDevStatNow == NULL || pDevStatPrev == NULL )
break;
if( pDevStatNow->id == NULL )
continue;
pGident = geom_lookupid( &m_stDiskGmesh, pDevStatNow->id );
if( pGident == NULL )
{
int i = 0;
geom_deletetree( &m_stDiskGmesh );
i = geom_gettree( &m_stDiskGmesh );
if( i != 0 )
{
// 루프를 빠져 나가도록 처리한다.
LOG( LERR, "geom_gettree() return error[%d]", i );
break;
}
pGident = geom_lookupid( &m_stDiskGmesh, pDevStatNow->id );
}
// ISCONSUMER 인 경우 제외
// - DEV/da7/da7, SWAP/swap/da12p1 등으로 표시됨.
// - 계산 대상에서 제외 처리함.
if( pGident == NULL || pGident->lg_what == gident::ISCONSUMER )
continue;
// physical provider ( rank = 1) 만 대상에 포함
// - da10p1, da12p3, gptid/2841d6b5-9659-11e4-9f9d-047d7b1a4e6 등의
// - 불필요한 항목을 대상에서 제외 처리한다.
if( pGident->lg_what == gident::ISPROVIDER
&& ( ( struct gprovider * )( pGident->lg_ptr ) )->lg_geom->lg_rank != 1 )
continue;
if( pDevStatNow->sequence0 != pDevStatNow->sequence1 )
continue;
// 추출된 Disk 정보의 Provider 정보가 유효하지 않은 경우...
// - 즉 표시되는 Name 정보가 유효하지 않는 것이므로...
// - 대상에서 제외 처리.
pProvider = ( struct gprovider * )( pGident->lg_ptr );
if( pProvider == NULL )
continue;
// Disk 정보 계산 처리.
devstat_compute_statistics( pDevStatNow, pDevStatPrev, dTimeInterval,
DSM_QUEUE_LENGTH, &ulQueueLength,
DSM_BUSY_PCT, &dBusy,
DSM_NONE );
// 추출된 값에서 최대값 정보를 추출한다.
if( ulQueueLength > ulMaxQueueLength )
ulMaxQueueLength = ulQueueLength;
// long double -> double 로 형변환할 경우... 소수점 이하 정밀도 부분에서 유실이 발생할 수 있지만...
// 기본적으로는 허용되는 형변환임.
if( dBusy > dMaxBusy )
dMaxBusy = (double)dBusy;
_LOG( LDBG, "disk status info: %s queue: %4ju, busy: %4.1Lf %%", pProvider->lg_name, ulQueueLength, dBusy );
// 2018-02-08 NEW huibong disk busy 상황시 해당 디스크 정보 로깅 (#31701)
// - Disky busy 상황시.. 실제 OS, Content 저장용 Disk 가 아닌 Raid Spare 디스크가 Busy 상황인 경우가 발견됨
// - 따라서 이를 좀더 정확히 확인하기 위해 Disk Busy 상황으로 판단되는 경우.. 해당 Disk 정보를 로깅하도록 처리한다.
if( dBusy >= DISK_BUSY_MIN_BUSY_PERCENT || ulQueueLength >= DISK_BUSY_MIN_QUEUE_COUNT )
{
_LOG( LINF, "disk busy: %s queue: %4ju busy: %4.1Lf %%", pProvider->lg_name, ulQueueLength, dBusy );
}
// 추출된 Data 를 Previous Data 에 저장 처리한다.
*pDevStatPrev = *pDevStatNow;
}
// 현재 추출한 shapshot 메모리 영역 해제 처리.
geom_stats_snapshot_free( m_pDiskSnapshotNow );
m_pDiskSnapshotNow = NULL;
// shared memory 저장 처리.
m_pFhsShm->SetDiskInfo( ulMaxQueueLength, dMaxBusy );
return true;
}
// Network 상태 정보 수집
bool CSystemInfo::GetNetworkStatus()
{
u_int uCount = 0;
size_t length = sizeof( uCount );
int arrayIfCount[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_SYSTEM, IFMIB_IFCOUNT };
int arrayIfInfo[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, 0 , IFDATA_GENERAL };
// Network Interface 갯수 정보 추출.
if( sysctl( arrayIfCount, 5, (void *)&uCount, (size_t *)&length, (void *)NULL, (size_t)0 ) != 0 )
{
// 정보 추출 실패시
// - 오류 로깅 후
// - shared memory 상의 network 통계 정보를 0 으로 설정 처리한다.
int errorNum = errno;
LOG( LERR, "network interface count collect error.[%d][%s]", errorNum, strerror( errorNum ) );
m_pFhsShm->SetNetworkInfo( 0, 0, 0, 0 );
return false;
}
// Network Interface 가 없는 경우...
if( uCount <= 0 )
{
LOG( LERR, "network interface count[%u] not valid", uCount );
// Network 통계 정보를 0 으로 설정 처리한다.
m_pFhsShm->SetNetworkInfo( 0, 0, 0, 0 );
return false;
}
// 추출된 정보를 저장하기 위한 변수
struct timespec timeNow;
memset( &timeNow, 0x00, sizeof( struct timespec ) );
uint64_t ulTotalPacketIn = 0;
uint64_t ulTotalPacketOut = 0;
uint64_t ulTotalByteIn = 0;
uint64_t ulTotalByteOut = 0;
struct ifmibdata stIfdata;
length = sizeof( struct ifmibdata );
// 추출된 count 숫자만큼 루프를 돌면서 Network interface 각 개별 정보를 수집 처리한다.
// - sysctl 호출 땜시 루프는 1 부터 시작.
for( int index = 1; index <= (int)uCount; index++ )
{
// 추출할 interface 번호 설정.
arrayIfInfo[4] = index;
// interface 세부 정보 추출.
if( ( sysctl( arrayIfInfo, 6, (void *)&stIfdata, (size_t *)&length, (void *)NULL, (size_t)0 ) != 0 ) && ( errno != ENOENT ) )
{
// 정보 추출 실패시
// - 오류 로깅 후
// - shared memory 상의 현재까지 추출, 합산된 정보만 set 처리.
int errorNum = errno;
LOG( LERR, "network interface info sysctl return error.[%d][%s]", errorNum, strerror( errorNum ) );
break;
}
// local loopback interface (lo) 제외
// - 장비의 트래픽 정보를 수집하는 것이므로.. lo 꺼는 빼는 것이 맞다.
if( strncmp( stIfdata.ifmd_name, "lo", 2 ) == 0 )
continue;
// link aggregation 처리되 lagg interface 제외
// - link ggreagtion 처리된 개별 interface 통계가 lagg 에 sum 되어 나타나므로... 통계 중복 발생.
// - 이를 해결하기 위해 lagg interface 는 수집 제외
if( strncmp( stIfdata.ifmd_name, "lagg", 4 ) == 0 )
continue;
_LOG( LDBG, "network interface collected: %s", stIfdata.ifmd_name );
// 추출 정보 합산 처리.
ulTotalPacketIn += stIfdata.ifmd_data.ifi_ipackets;
ulTotalPacketOut += stIfdata.ifmd_data.ifi_opackets;
ulTotalByteIn += stIfdata.ifmd_data.ifi_ibytes;
ulTotalByteOut += stIfdata.ifmd_data.ifi_obytes;
}
// 최종 시간 정보 추출.
clock_gettime( CLOCK_MONOTONIC, &timeNow );
// 이전에 추출된 Network 통계 정보와의 계산을 통해서...
// 실제 트래픽, packet 갯수 등을 추출.
if( m_timeNetworkPrev.tv_sec == 0 && m_timeNetworkPrev.tv_nsec == 0 )
{
// 최초 기동 후 이전 Data 가 아직 수집되지 않은 경우...
// - 통계 정보 계산이 불가능하므로...
// - 0 으로 정보 설정 처리한다.
m_pFhsShm->SetNetworkInfo( 0, 0, 0, 0 );
}
else
{
// 이전 추출 시간과 현재 시간 간의 시간 간격 계산.
double dTimeInterval = 0.0;
dTimeInterval = timeNow.tv_sec - m_timeNetworkPrev.tv_sec;
dTimeInterval += ( timeNow.tv_nsec - m_timeNetworkPrev.tv_nsec ) * 1e-9;
uint64_t ulPacketIn = ulTotalPacketIn - m_ulNetworkPacketIn;
uint64_t ulPacketOut = ulTotalPacketOut - m_ulNetworkPacketOut;
uint64_t ulByteIn = ulTotalByteIn - m_ulNetworkByteIn;
uint64_t ulByteOut = ulTotalByteOut - m_ulNetworkByteOut;
// 평균 packet count(count/sec), 트래픽(Byte/sec)은 반올림 또는 정수 올림 처리하지 않고.. 계산된 값의 정수 부분만 사용 처리한다.
uint64_t ulPacketInAvg = 0;
uint64_t ulPacketOutAvg = 0;
uint64_t ulTrafficIn = 0;
uint64_t ulTrafficOut = 0;
if( dTimeInterval > 0.0 )
{
ulPacketInAvg = (uint64_t)( ulPacketIn / dTimeInterval );
ulPacketOutAvg = (uint64_t)( ulPacketOut / dTimeInterval );
ulTrafficIn = (uint64_t)( ulByteIn / dTimeInterval );
ulTrafficOut = (uint64_t)( ulByteOut / dTimeInterval );
}
// Shared Memory 상에 저장 처리.
m_pFhsShm->SetNetworkInfo( ulPacketInAvg, ulPacketOutAvg, ulTrafficIn, ulTrafficOut );
}
// 멤버 변수에 현재 추출된 정보를 저장 처리한다.
m_timeNetworkPrev.tv_sec = timeNow.tv_sec;
m_timeNetworkPrev.tv_nsec = timeNow.tv_nsec;
m_ulNetworkPacketIn = ulTotalPacketIn;
m_ulNetworkPacketOut = ulTotalPacketOut;
m_ulNetworkByteIn = ulTotalByteIn;
m_ulNetworkByteOut = ulTotalByteOut;
return true;
}
// TCP session 상태 정보 수집
bool CSystemInfo::GetTcpSession()
{
unsigned int uTcpTotal = 0;
unsigned int uTcp80 = 0;
unsigned int uTcp80Establish = 0;
size_t length = 0;
// 세션 갯수 정보 추출.
if( sysctlbyname( "net.inet.tcp.pcblist", 0, &length, 0, 0 ) < 0 )
{
// 함수 호출 실패시
// - 오류 내역 로깅 후
// - shraed memory 상의 설정 정보를 0 으로 초기화.
int errorNum = errno;
if( errorNum == ENOENT )
{
LOG( LERR, "tcp session count collect error. OS version check[%d][%s]", errorNum, strerror( errorNum ) );
}
else
{
LOG( LERR, "tcp session count collect error.[%d][%s]", errorNum, strerror( errorNum ) );
}
m_pFhsShm->SetTcpInfo( 0, 0, 0 );
return false;
}
// 추출된 세션 정보를 저장할 임시 buf 할당.
char * szBuffer = NULL;
szBuffer = (char *)malloc( length );
if( szBuffer == NULL )
{
// 임시 buffer 생성 실패시..
int errorNum = errno;
LOG( LERR, "tcp session collect error. malloc fail.[%lu]Byte, [%d][%s]", length, errorNum, strerror( errorNum ) );
m_pFhsShm->SetTcpInfo( 0, 0, 0 );
return false;
}
// 세션 정보 추출.
if( sysctlbyname( "net.inet.tcp.pcblist", szBuffer, &length, 0, 0 ) < 0 )
{
// 세션 정보 추출 실패시
int errorNum = errno;
LOG( LERR, "tcp session collect error.[%d][%s]", errorNum, strerror( errorNum ) );
free( szBuffer );
m_pFhsShm->SetTcpInfo( 0, 0, 0 );
return false;
}
// 추출된 정보를 루프를 돌면서 count 처리한다.
// netstat 에서 사용된 source 코드를 거의 그대로 사용.
struct xinpgen *xig, *oxig;
struct xsocket *so;
// CHG 2020-10-29 huibong FreeBSD 12.x 지원을 위한 코드 분리 (#33233)
// - FreeBSD 12.x 에서 include/netinet/tcp_var.h 에서 tcpcb 구조체 변경이 발생
// - 이로 인해 기존 FreeBSD 6.x, 10.x 에서 동작하는 TCP 세션 정보 추출 코드가 컴파일 되지 않는 문제 발생
// - FreeBSD 12.x 의 /usr/src/usr.bin/netstat/inet.c 소스를 참고로..
// - FreeBSD 6.x, 10.x 와 FreeBSD 12.x 에서 분리된 코드로 컴파일 되도록 수정 처리함.
#if __FreeBSD_version < 1200000
struct inpcb *inp;
struct tcpcb *tp = NULL;
#else
struct xinpcb *inp;
struct xtcpcb *tp;
#endif
int port;
oxig = xig = ( struct xinpgen * )szBuffer;
for( xig = ( struct xinpgen * )( (char *)xig + xig->xig_len );
xig->xig_len > sizeof( struct xinpgen );
xig = ( struct xinpgen * )( (char *)xig + xig->xig_len ) )
{
// CHG 2020-10-29 huibong FreeBSD 12.x 지원을 위한 코드 분리 (#33233)
// - FreeBSD 12.x 에서 include/netinet/tcp_var.h 에서 tcpcb 구조체 변경이 발생
// - 이로 인해 기존 FreeBSD 6.x, 10.x 에서 동작하는 TCP 세션 정보 추출 코드가 컴파일 되지 않는 문제 발생
// - FreeBSD 12.x 의 /usr/src/usr.bin/netstat/inet.c 소스를 참고로..
// - FreeBSD 6.x, 10.x 와 FreeBSD 12.x 에서 분리된 코드로 컴파일 되도록 수정 처리함.
#if __FreeBSD_version < 1200000
so = &( ( struct xtcpcb * )xig )->xt_socket;
inp = &( ( struct xtcpcb * )xig )->xt_inp;
tp = &( ( struct xtcpcb * )xig )->xt_tp;
#else
tp = (struct xtcpcb *)xig;
inp = &tp->xt_inp;
so = &inp->xi_socket;
#endif
// Ignore sockets for protocols other than the desired one.
if( so->xso_protocol != IPPROTO_TCP )
continue;
// Ignore PCBs which were freed during copyout.
if( inp->inp_gencnt > oxig->xig_gen )
continue;
// IP v4, v6 가 아닌 경우 제외
if( ( inp->inp_vflag & INP_IPV4 ) == 0 && ( inp->inp_vflag & INP_IPV6 ) == 0 )
continue;
uTcpTotal++;
// Local port 정보 주출.
port = ntohs( (u_short)inp->inp_lport );
if( port == 80 )
{
uTcp80++;
if( tp && tp->t_state == TCPS_ESTABLISHED )
uTcp80Establish++;
}
}
// malloc 으로 할당된 임시 버퍼 해제 처리.
free( szBuffer );
// shared memory update.
m_pFhsShm->SetTcpInfo( uTcpTotal, uTcp80, uTcp80Establish );
return true;
}
+112
View File
@@ -0,0 +1,112 @@
/****************************************************************************
system infomation collect & shared memory set class
-----------------------------------------
begin : 2015/04/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 __SYSTEM_INFO_H__
#define __SYSTEM_INFO_H__
#include "FhsShm.h"
#include "StorageInfo.h"
#include <unistd.h>
#include <kvm.h>
#include <libgeom.h>
#include <string>
// System 정보를 추출하여.. 공유메모리에 저장을 담당하기 위한 class
class CSystemInfo
{
public:
// 생성자
CSystemInfo( CFhsShm * pFhsShm );
// 소멸자
~CSystemInfo();
// 초기화 함수
bool Init();
public: // 시스템 정보 수집 관련 함수
// hostname 정보 수집
bool GetHostname();
// load average 정보 수집
bool GetLoadAverage();
// total memory 정보 수집
bool GetMemoryTotal();
// free memory 정보 수집
bool GetMemoryFree();
// swap 정보 수집
bool GetSwap();
// mount 정보 수집 및 처리
bool GetMount( const char * szRoot);
// Disk 상태 정보 수집
bool GetDiskStatus();
// Network 상태 정보 수집
bool GetNetworkStatus();
// TCP session 상태 정보 수집
bool GetTcpSession();
private: // 시스템 정보 수집 관련 멤버 변수
// 공유메모리 interface 객체에 대한 포인터.
// - 공유메모리에 Data Set, Get 을 담당
CFhsShm * m_pFhsShm;
// Kernel data access library 객체에 대한 포인터
// - swap 정보 추출을 위해 사용.
kvm_t * m_pKvm;
// Disk 상태 정보 수집을 위한 멤버변수
struct gmesh m_stDiskGmesh; // gmp
void * m_pDiskSnapshotPrev; // sq
void * m_pDiskSnapshotNow; // sp
struct timespec m_timeDiskPrev; // tq
struct timespec m_timeDiskNow; // tp
// Network 통계 정보 수집을 위한 멤버 변수
struct timespec m_timeNetworkPrev; // 최종 Network 통계 수집 시간.
uint64_t m_ulNetworkPacketIn; // total network in packet count
uint64_t m_ulNetworkPacketOut; // total network out packet count
uint64_t m_ulNetworkByteIn; // total network in byte
uint64_t m_ulNetworkByteOut; // total network out byte
// Storage mount 정보 추출 관련 처리를 수행할 class 객체
CStorageInfo m_storageInfo;
private: // 처리 관련 내부 함수
};
#endif /* __SYSTEM_INFO_H__ */
+506
View File
@@ -0,0 +1,506 @@
#include "TelegrafControl.h"
#include "Logger.h"
#include "BaseSocket.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/types.h>
#include <signal.h>
#include <string>
#define TELEGRAF_PROCESS_NAME "telegraf"
#define TELEGRAF_BINARY_FULL_PATH "/usr/local/bin/telegraf"
#define TELEGRAF_PROMETHEUS_CLIENT_IP "127.0.0.1"
#define TELEGRAF_PROMETHEUS_CLIENT_PORT 9200
CTelegrafControl::CTelegrafControl()
{
}
CTelegrafControl::~CTelegrafControl()
{
}
// telegraf process 의 PID 정보 반환...
// - 정보 추출시 오류 또는 미동작 상황인 경우.. 0 반환
int CTelegrafControl::GetProcessId()
{
char tempBuffer[64];
FILE * fd = NULL;
int iProcessId = 0;
snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -x %s", TELEGRAF_PROCESS_NAME );
fd = popen( tempBuffer, "r" );
if( fd == NULL )
{
// popen 수행 실패시......
int errorNum = errno;
LOG( LERR, "[TelegrafControl] popen failed. [%d][%s]", errorNum, strerror( errorNum ) );
// 오류 발생시 0 반환
iProcessId = 0;
}
else
{
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
// 참고사항
// https://jarvis.solbox.com/redmine/issues/32973 에서 언급된 popen - fgets() 호출시 지연 발생 가능성 관련
// 서비스 자체에는 영향이 없고... 호출 빈도를 최소화하도록 조정하여....
// 사용하기로 결정함.
if( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
{
// 프로세스 ID 가 존재하는 경우
std::string tempPid( tempBuffer );
Trim( tempPid );
iProcessId = atoi( tempPid.c_str() );
_LOG( LDBG, "[TelegrafControl] %s process id %d.", TELEGRAF_PROCESS_NAME, iProcessId );
}
pclose( fd );
}
return iProcessId;
}
// telegraf service start
// - system() 호출 관련 service 명령어 수행시.. -1 이 반환되므로....
// - telegraf 가 정상 기동 되었는지 판단 불가하여.. 반환값은 void 로 처리한다.
void CTelegrafControl::ServiceStart()
{
// 우선 binary 가 정상적으로 존재하는지 check
// - FreeBSD 6.x, 10.0 버전에서는 telegraf 동작시키지 않으므로... binary 가 없는 경우.. 기동시키지 않는다.
if( access( TELEGRAF_BINARY_FULL_PATH, X_OK ) != 0 )
{
_LOG( LWAR, "[TelegrafControl] binary[%s] not exist.", TELEGRAF_BINARY_FULL_PATH );
return;
}
// service 명령을 이용하여... telegraf 를 기동 처리한다.
// - service 명령어로 기동시... system() 호출 결과는 -1, errno=10 (No child processes) 가 반환된다.
// - 따라서 system() 호출 결과 -1 을 반환으로 인해.. telegraf 가 정상 기동되었는지 판단 불가하므로...
// - 본 함수의 반환값은 void 로 처리한다.
system( "service telegraf start" );
_LOG( LINF, "[TelegrafControl] command \'service telegraf start\' executed.");
return;
}
// telegraf 가 hang 상태인지 check
// - hang 상황인 경우 true 반환
// - 그 외의 경우에는 false 반환
bool CTelegrafControl::IsHang()
{
// telegraf 의 prometheus_client 와 통신을 통하여.. telegraf 가 hang 상태인지 check 한다.
// [[outputs.prometheus_client]]
// listen = ":9200"
// telegraf 가
// TCP 9200 port 접속 후 전달한 잘못된 요청에 대해.. HTTP 400 Bad Request 등과 같은 어떠한 응답이라도 하면 정상..
// 그 외에는 hang 상황으로 판단한다.
// - 접속은 되나... 지정된 시간동안 응답을 수신하지 못할 경우...
// - 아예 접속이 안되는 경우.. ( Listen queue overflow 상황)
std::string strTragetIp = TELEGRAF_PROMETHEUS_CLIENT_IP;
// socket 관련 변수.
int sock = -1;
struct sockaddr_in stTarget;
int flags;
int result = 0;
int errorNum = 0;
// socket 생성.
sock = socket( AF_INET, SOCK_STREAM, 0 );
if( sock == -1 )
{
errorNum = errno;
_LOG( LERR, "[TelegrafControl] socket create error. [%d][%s]", errorNum, strerror( errorNum ) );
// hang check 수행하지 못했으므로.. 우선 정상 상태로 판단.
return false;
}
// Connnection 종료시 많은 TIME_WAIT 상태 발생 방지를 위해 SO_LINGER 옵션을 설정처리한다.
struct linger opt_linger;
opt_linger.l_onoff = 1; /* LINGER ON */
// 0 : No Wait for abortive disconnect
// - 0 으로 설정할 경우. close 처리시.. 상대편에서 정상 종료를 제대로 인지 못할 수 있음
// - 0 이외 정수값으로 설정할 경우.. TCP TIME_WAIT 이 발생하며... 60 sec 후 정리됨.
// - 우선 check 주기가 1 min 정도 이므로.. 많은 세션이 발생하지 않아... 1 로 설정한다.
opt_linger.l_linger = 3;
if( setsockopt( sock, SOL_SOCKET, SO_LINGER, &opt_linger, sizeof( opt_linger ) ) != 0 )
{
// 오류가 발생한 경우..
// - 관련 내역 로깅만 처리하고 check 작업은 그대로 진행한다.
errorNum = errno;
_LOG( LWAR, "[TelegrafControl] setsockopt SO_LINGER set error. [%d][%s]", errorNum, strerror( errorNum ) );
}
memset( &stTarget, 0x00, sizeof( struct sockaddr_in ) );
stTarget.sin_family = AF_INET;
stTarget.sin_port = htons( TELEGRAF_PROMETHEUS_CLIENT_PORT );
stTarget.sin_addr.s_addr = inet_addr( strTragetIp.c_str() );
// connection timeout 관련 처리
// - 접속 대상 장비가 부하 상황인 경우... connect() 함수의 block 발생
// - 이를 해결하기 위해 connection timeout 를 구현
// - non block socket 으로 만든 후.. connect 호출. select 함수를 호출하여 정상 처리 여부 확인.
/* Non Block Mode Set */
if( ( flags = fcntl( sock, F_GETFL, 0 ) ) >= 0 )
{
if( fcntl( sock, F_SETFL, flags | O_NONBLOCK ) < 0 )
{
errorNum = errno;
_LOG( LWAR, "[TelegrafControl] socket NONBLOCK set error [%d][%s]", errorNum, strerror( errorNum ) );
close( sock );
// hang check 수행하지 못했으므로.. 우선 정상 상태로 판단.
return false;
}
}
else
{
// fcntl 호출 오류시
errorNum = errno;
_LOG( LWAR, "[TelegrafControl] socket fcntl get error [%d][%s]", errorNum, strerror( errorNum ) );
close( sock );
// hang check 수행하지 못했으므로.. 우선 정상 상태로 판단.
return false;
}
// 접속 시도
result = connect( sock, (struct sockaddr *)&stTarget, sizeof( stTarget ) );
if( result < 0 )
{
// 중요 코드
// - connect 시도 후 결과를 바로 확인하지 않고... select 함수에서 결과 확인시..
// - 오류 코드 clear 발생 등의 문제로 인해...
// - 실제 접속이 되지 않았는데.. 접속된 것으로 처리 될 수 있음.
// - 이를 방지하기 위해 본 코드가 중요함.
errorNum = errno;
// non-block 모드이므로.. 처리 중인 경우 제외하고 오류로 처리.
if( errorNum != EINPROGRESS )
{
_LOG( LINF, "[TelegrafControl] telegraf connect fail.[%s][%d]->[%d][%s]"
, strTragetIp.c_str(), TELEGRAF_PROMETHEUS_CLIENT_PORT, errorNum, strerror( errorNum ) );
close( sock );
// 접속 실패이므로.. hang 으로 판단.
return true;
}
}
// 바로 접속이 된 경우...
if( result == 0 )
{
// 관련 내역 로깅
_LOG( LDBG, "[TelegrafControl] telegraf connect ok. ip[%s][%d]", strTragetIp.c_str(), TELEGRAF_PROMETHEUS_CLIENT_PORT );
}
else
{
// 바로 접속이 되지 않은 경우....
// select 함수를 이용하여 timeout 여부 확인.
// connect timeout 설정 관련 변수
fd_set selectReadFds, selectWriteFds;
struct timeval timeOver;
FD_ZERO( &selectReadFds );
FD_SET( sock, &selectReadFds );
selectWriteFds = selectReadFds;
timeOver.tv_sec = 2; /* 2 second timeout */
timeOver.tv_usec = 0;
// select 함수를 이용한 접속 결과 관련 처리.
if( select( sock + 1, &selectReadFds, &selectWriteFds, NULL, &timeOver ) == 1 )
{
errorNum = 0;
socklen_t errorLength = sizeof( errorNum );
if( getsockopt( sock, SOL_SOCKET, SO_ERROR, &errorNum, &errorLength ) == -1 )
{
// 오류 발생시.
errorNum = errno;
_LOG( LERR, "[TelegrafControl] telegraf connect fail. getsockopt SO_ERROR get error [%d][%s] ip[%s][%d]"
, errorNum, strerror( errorNum ), strTragetIp.c_str(), TELEGRAF_PROMETHEUS_CLIENT_PORT );
close( sock );
// 접속 실패로 판단하여.. hang 상태로 처리한다.
return true;
}
else
{
if( errorNum == 0 )
{
// 접속 성공시.
_LOG( LDBG, "[TelegrafControl] telegraf connect[wait] ok. ip[%s][%d]", strTragetIp.c_str(), TELEGRAF_PROMETHEUS_CLIENT_PORT );
}
else
{
// 접속 실패시.
_LOG( LINF, "[TelegrafControl] telegraf connect[wait] fail.[%s][%d]->[%d][%s]", strTragetIp.c_str(), TELEGRAF_PROMETHEUS_CLIENT_PORT, errorNum, strerror( errorNum ) );
close( sock );
// 접속 실패로 판단하여.. hang 상태로 처리한다.
return true;
}
}
}
else
{
// connect timeout or select 함수 오류 발생시
// - 접속 실패로 처리.
_LOG( LINF, "[TelegrafControl] telegraf connect timeout.[%s][%d]", strTragetIp.c_str(), TELEGRAF_PROMETHEUS_CLIENT_PORT );
close( sock );
// 접속 실패로 판단하여.. hang 상태로 처리한다.
return true;
}
}
// request 전송을 위해 socket 을 blocking 모드로 다시 전환
if( fcntl( sock, F_SETFL, flags ) < 0 )
{
errorNum = errno;
_LOG( LWAR, "[TelegrafControl] socket BLOCK set error [%d][%s]", errorNum, strerror( errorNum ) );
close( sock );
// 접속은 정상이지만... hang check 수행하지 못했으므로.. 우선 정상 상태로 판단.
return false;
}
// send time out 설정
// - request 전송시 timeout 설정 목적.
struct timeval tv_timeo = { 2, 0 };
result = setsockopt( sock, SOL_SOCKET, SO_SNDTIMEO, &tv_timeo, sizeof( tv_timeo ) );
if( result != 0 )
{
errorNum = errno;
_LOG( LERR, "[TelegrafControl] socket SO_SNDTIMEO set fail. [%d][%s]", errorNum, strerror( errorNum ) );
}
// data 전송, 수신 처리 단순화를 위해 basesocket 객체 이용
CBaseSocket basesock( sock );
// request 전송
std::string strMsg = "GET /hang_check\r\n";
long lResult = basesock.WriteN( strMsg.c_str(), strMsg.size() );
if( lResult <= 0 )
{
// 전송 실패시...
_LOG( LERR, "[TelegrafControl] telegraf request send fail. [%ld]", lResult );
basesock.Close();
// 전송 실패가 발생했으므로.. hang 상태로 판단.
return true;
}
// 수신 대기
char buffer[64];
memset( buffer, 0x00, 64 );
// HTTP/1.1 400 Bad Request 응답만 수신 시도, timeout 은 3 sec
lResult = basesock.ReadNTimeout( buffer, 24, 3 );
if( lResult <= 0 )
{
// 응답 수신 실패시....
if( lResult == -2 )
{
// timeout 발생시 명확하게 로깅 처리.
_LOG( LERR, "[TelegrafControl] telegraf response receive error. [timeout 3 sec]");
}
else
{
_LOG( LERR, "[TelegrafControl] telegraf response receive error. [%ld]", lResult );
}
basesock.Close();
// 수신 실패가 발생했으므로.. hang 상태로 판단.
return true;
}
else
{
// 정상 응답 수신한 경우..
_LOG( LDBG, "[TelegrafControl] telegraf response recevied. [%s]", buffer );
}
// 사용한 socket 종료 처리.
basesock.Close();
// telegraf 정상인 상태로 판단한다.
return false;
}
// telegraf SIGQUIT signal 전송
// - log 상에 stack trace 남기고 재기동 처리됨
// - hang 상황시.. trace 처리 후 재기동 처리하기 위한 함수.
void CTelegrafControl::ServiceQuit()
{
int pid = GetProcessId();
if( pid != 0 )
{
// telegraf 에 SIGQUIT 전송시.. log 상에 statck trace 정보 남기고.. 자동 재기동 됨..
// - hang 상태에서도 SIGQUIT 전송시.. 위의 내역대로 정상 동작함.
kill( pid, SIGQUIT );
_LOG( LINF, "[TelegrafControl] SIGQUIT send to restart telegraf[%d] in hang state.", pid );
}
else
{
_LOG( LERR, "[TelegrafControl] telegraf pid for restarting not valid. [%d]", pid );
}
return;
}
// telegraf 의 기동 후 elapsed time 정보를 추출한다.
// - ps -o etimes= -p `pgrep -x telegraf` 명령을 이용하여 추출
// - 0 인 경우.. 오류로 판단...
long long CTelegrafControl::GetProcessElapsedSec()
{
int pid = GetProcessId();
if( pid == 0 )
{
LOG( LERR, "[TelegrafControl] telegraf pid for elapsed time not valid. [%d]", pid );
// process id 정보가 유효하지 않은 경우.. 오류로 처리
return 0;
}
else
{
char tempBuffer[64];
FILE * fd = NULL;
long long llElapsedSec = 0;
snprintf( tempBuffer, sizeof( tempBuffer ), "ps -o etimes= -p %d", pid );
fd = popen( tempBuffer, "r" );
if( fd == NULL )
{
// popen 수행 실패시......
int errorNum = errno;
LOG( LERR, "[TelegrafControl] popen failed by ps -o etimes=. [%d][%s]", errorNum, strerror( errorNum ) );
// 오류 발생시 0 반환
return 0;
}
else
{
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
// 참고사항
// https://jarvis.solbox.com/redmine/issues/32973 에서 언급된 popen - fgets() 호출시 지연 발생 가능성 관련
// 서비스 자체에는 영향이 없고... 호출 빈도를 최소화하도록 조정하여....
// 사용하기로 결정함.
if( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
{
// result 값이 존재하는 경우
std::string tempResult( tempBuffer );
Trim( tempResult );
llElapsedSec = atoll( tempResult.c_str() );
_LOG( LDBG, "[TelegrafControl] %s process elapsed time %lld sec.", TELEGRAF_PROCESS_NAME, llElapsedSec );
}
pclose( fd );
}
return llElapsedSec;
}
return 0;
}
// telegraf service restart
// - ServiceStart() 함수와 마찬가지로.. service 명령어 호출시. .무조건 -1 이 반환되므로....
// - telegraf 가 정상적으로 restart 되었는지 판단 불가하여.. void 로 처리
void CTelegrafControl::ServiceRestart()
{
// 우선 binary 가 정상적으로 존재하는지 check
if( access( TELEGRAF_BINARY_FULL_PATH, X_OK ) != 0 )
{
_LOG( LWAR, "[TelegrafControl] binary[%s] not exist.", TELEGRAF_BINARY_FULL_PATH );
return;
}
// service 명령을 이용하여... telegraf 를 기동 처리한다.
// - service 명령어로 기동시... system() 호출 결과는 -1, errno=10 (No child processes) 가 반환된다.
// - 따라서 system() 호출 결과 -1 을 반환으로 인해.. telegraf 가 정상 기동되었는지 판단 불가하므로...
// - 본 함수의 반환값은 void 로 처리한다.
system( "service telegraf restart" );
_LOG( LINF, "[TelegrafControl] command \'service telegraf restart\' executed." );
return;
}
///////////////////////////////////////////////////////////////////////
void CTelegrafControl::Trim( std::string & str )
{
if( str.length() == 0 )
return;
// 문자열 뒤의 공백, TAB, CR 등의 문자 제거처리.
std::string::size_type pos = str.find_last_not_of( " \a\b\f\n\r\t\v" );
if( pos != std::string::npos )
str.erase( pos + 1 );
// 문자열 앞의 공백, TAB, CR 등의 문자 제거처리.
pos = str.find_first_not_of( " \a\b\f\n\r\t\v" );
if( pos != std::string::npos )
str.erase( 0, pos );
}
+73
View File
@@ -0,0 +1,73 @@
/****************************************************************************
Telegraf control calss
-----------------------------------------
begin : 2021/05/10
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __TELEGRAF_CONTROL_H__
#define __TELEGRAF_CONTROL_H__
#include <unistd.h>
#include <string>
// CTelegrafControl class
// telegraf 에 대한 alive 상태 check, 재기동 처리 등의 action 을 수행하기 위한 class
// 본 class 는 telegraf 관련 interface 만 제공하고...
// 실제 판단 및 명령은 상위 worker 프로세스 상에서 수행한다.
class CTelegrafControl
{
public:
// 생성자
CTelegrafControl();
// 소멸자
~CTelegrafControl();
// telegraf process 의 PID 정보 반환...
// - 정보 추출시 오류 또는 미동작 상황인 경우.. 0 반환
int GetProcessId();
// telegraf service start
void ServiceStart();
// telegraf hang check
bool IsHang();
// telegraf SIGQUIT signal 전송
// - log 상에 stack trace 남기고 재기동 처리됨
// - hang 상황시.. trace 처리 후 재기동 처리하기 위한 함수.
void ServiceQuit();
// telegraf 의 기동 후 elapsed time 정보를 추출한다.
// - ps -o etimes= -p `pgrep -x telegraf` 명령을 이용하여 추출
// - 0 인 경우.. 오류로 판단...
long long GetProcessElapsedSec();
// telegraf service restart
void ServiceRestart();
private:
void Trim( std::string & str );
};
#endif /* __TELEGRAF_CONTROL_H__ */
@@ -0,0 +1,425 @@
#include "WorkerMonitoringPortControl.h"
#include "ProcessConfig.h"
#include "Logger.h"
#include "FhsShm.h"
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// Service status Check 주기 (sec)
// - Service status 에 따른 Listen Port 제어 관련 Loop 주기
#define SERVICE_STATUS_CHECK_INTERVAL 2
// accept() 처리 관련 timeout (sec)
#define ACCEPT_TIMEOUT_SEC 2
// listen queue 에서 accept 대기 Count 수
// - 기본 적으로 Monitoring 용 Listen Port 이므로... 갯수를 적게 유지 처리한다.
#define DEFAULT_LISTEN_WAIT_COUNT 5
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main 프로세스에서 생성 처리하며... 전역변수로 설정되어 있다.
extern CFhsShm * g_pFhsShm;
// Listener Socket
// 시그널 등의 전역 처리 문제로 전역 변수로 사용
int g_listenSocket = -1;
// Listen 처리를 위한 socket 생성
// @param listenSocket [out] 생성된 Socket 정보를 저장하기 위한 참조변수
// @return 성공시 true, 오류발생시 false 반환.
bool MakeListenSocketV6( int & listenSocket )
{
// socket 생성
listenSocket = socket( AF_INET6, SOCK_STREAM, 0 );
if( listenSocket == -1 )
{
int errorNum = errno;
LOG( LERR, "[MonPortControl] listen socket create failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
struct sockaddr_in6 listenSockAddr;
socklen_t listenSockLen = 0;
memset( &listenSockAddr, 0x00, sizeof( listenSockAddr ) );
listenSockAddr.sin6_family = AF_INET6;
listenSockAddr.sin6_flowinfo = 0;
listenSockAddr.sin6_port = htons( CProcessConfig::GetInstance()->GetListenPortForStatusMonitoring() );
listenSockAddr.sin6_addr = in6addr_any;
listenSockLen = sizeof( listenSockAddr );
int result = 0;
int opt = 0;
// socket address reuse option set
opt = 1;
result = setsockopt( listenSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof( opt ) );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "[MonPortControl] listen socket option[SO_REUSEADDR] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
close( listenSocket );
listenSocket = -1;
return false;
}
// keep alive option set
opt = 1;
result = setsockopt( listenSocket, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "[MonPortControl] listen socket option[SO_KEEPALIVE] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
close( listenSocket );
listenSocket = -1;
return false;
}
// IPv6 에서 IPv4 지원을 위한 설정
opt = 0;
result = setsockopt( listenSocket, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof( opt ) );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "[MonPortControl] listen socket option[IPV6_V6ONLY] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
close( listenSocket );
listenSocket = -1;
return false;
}
// socket bind
result = bind( listenSocket, ( struct sockaddr * )&listenSockAddr, listenSockLen );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "[MonPortControl] listen socket bind failed. [%d][%s]", errorNum, strerror( errorNum ) );
close( listenSocket );
listenSocket = -1;
return false;
}
// socket listen
result = listen( listenSocket, DEFAULT_LISTEN_WAIT_COUNT );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "[MonPortControl] listen socket listen failed. [%d][%s]", errorNum, strerror( errorNum ) );
close( listenSocket );
listenSocket = -1;
return false;
}
_LOG( LINF, "[MonPortControl] tcp port[%u] listen start."
, CProcessConfig::GetInstance()->GetListenPortForStatusMonitoring() );
return true;
}
// Listen 처리를 위한 socket close 처리
// @param listenSocket [in] listen socket 정보
void CloseListenSocket( int & listenSocket )
{
if( listenSocket != -1 )
{
close( listenSocket );
listenSocket = -1;
}
return;
}
// Worker::MonPortControl 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerMonPortControlMain()
{
setproctitle( "MonPortControl [initialize]" );
// Set Signal Handler
SetWorkerMonPortControlSignalHandler();
// Main Process 에서 생성한 공유메모리 접근 객체 유효성 확인
// NULL 인 경우... 초기화 실패로 처리한다.
if( g_pFhsShm == NULL )
{
// 객체 생성 실패시
LOG( LERR, "[MonPortControl:%d] Shared memory object not valid. process exit", getpid() );
ReadyToExitWorkerMonPortControl();
return EXIT_FAILURE;
}
// sleep 처리 관련 설정 처리
struct timespec sleepLoop;
sleepLoop.tv_sec = SERVICE_STATUS_CHECK_INTERVAL;
sleepLoop.tv_nsec = 0;
// 서비스 상태 Check 관련 변수
int iServiceStat;
time_t timeServiceCheck = 0;
struct timespec currentClock;
// select 관련 변수
struct timeval selectTimeOut;
int iSelectResult;
fd_set selectFds;
FD_ZERO( &selectFds );
// socket 처리 관련 변수
int errorNum;
char tempBuffer[INET6_ADDRSTRLEN];
int clientSocket;
struct sockaddr_in6 clientSockAddr;
socklen_t clientSockLen = sizeof( clientSockAddr );
// 전체 프로세스의 생성이 완료될때까지 대기 후 작업 시작
setproctitle( "MonPortControl [TCP %u]", CProcessConfig::GetInstance()->GetListenPortForStatusMonitoring() );
sleep( 3 );
// Listen 처리 관련 FreeBSD 10.X , 6.X 차이점
// - FB 10.X 의 경우.. listen 상태에서 accept() 를 호출하지 않아도.. neocast 에서 정상 상태로 인식
// - FB 6.X 의 경우.. neocast 에서 listen 만 하면 비정상 상태로 인식하고... accept 까지 해야 정상 상태로 인식됨.
// - 이러한 차이로 인해 accept() 를 하지 않는 방법으로 Port 제어를 하는 것으로 적합하지 않음.
// - 이에 따라 서비스 상태에 따른 Listen Socket 생성, Close 처리 방법을 사용한다.
// 주기적으로 Service Status 정보를 Check 한다.
// - 서비스 상태 Check 프로세스에서 blocking 상황 발생시
// - RCTS 전송 프로세스에서 Timeout 처리를 수행하여 서비스 상태를 Disable 로 처리한다.
// - 하지만 RCTS 전송 프로세스에서도 blocking 상황 발생하면.. 서비스 상태 변경이 안되서..
// - 모니터링 Port 관련 제어가 불가하므로... 보조적으로 RCTS 전송 프로세스에 적용한 Timeout Check 기능을 넣는다.
while( 1 )
{
iServiceStat = g_pFhsShm->GetServiceStat();
if( iServiceStat == SERVICE_STAT_DISABLE || iServiceStat == SERVICE_STAT_OUT )
{
// 서비스 상태가 out, disable 상태인 경우....
// - monitoring port close 처리한다.
if( g_listenSocket != -1 )
{
CloseListenSocket( g_listenSocket );
_LOG( LINF, "[MonPortControl] service stat [%s]. TCP monitoring port[%u] closed."
, g_pFhsShm->GetServiceStatString( iServiceStat )
, CProcessConfig::GetInstance()->GetListenPortForStatusMonitoring() );
}
// 지정된 시간동안 sleep 한 후 다시 check 수행
nanosleep( &sleepLoop, NULL );
continue;
}
else
{
// 서비스 상태가 정상인 상황인 경우...
// - 서비스 상태 check 가 정상적으로 동작 중인지 체크하여
// - 서비스 상태 check 가 blocking 상황이면.. 자체적으로 모니터링 Port 를 차단한다.
timeServiceCheck = g_pFhsShm->GetTimeServiceCheck();
if( timeServiceCheck == 0 )
{
// 아직 초기화 중인 상태
// - Port 관련 처리를 수행하지 않고.. 지정된 시간 sleep 후 다시 check 수행한다.
nanosleep( &sleepLoop, NULL );
continue;
}
else
{
// 현재 시간을 추출
memset( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
if( timeServiceCheck < currentClock.tv_sec - TIMEOUT_SERVICE_CHECK )
{
// timeout 발생시 서비스 Disable 상태로 변경 처리
g_pFhsShm->SetServiceStat( SERVICE_STAT_DISABLE );
_LOG( LERR, "[MonPortControl] service check timeout[%d] last[%ld], current[%ld]. service stat set: %d (%s)"
, TIMEOUT_SERVICE_CHECK, timeServiceCheck, currentClock.tv_sec
, g_pFhsShm->GetServiceStat(), g_pFhsShm->GetServiceStatString() );
// 서비스 상태가 out, disable 상태이므로... monitoring port close 처리한다.
if( g_listenSocket != -1 )
{
CloseListenSocket( g_listenSocket );
_LOG( LINF, "[MonPortControl] service stat timeout changed [%s]. TCP monitoring port[%u] closed."
, g_pFhsShm->GetServiceStatString( iServiceStat )
, CProcessConfig::GetInstance()->GetListenPortForStatusMonitoring() );
}
// 지정된 시간동안 sleep 한 후 다시 check 수행
nanosleep( &sleepLoop, NULL );
continue;
}
}
}
// 서비스 상태가 정상 또는 Read Only 상태인 경우
// Listen Socket 이 이미 존재하는지 체크하여...
// - Listen Socket 가 생성되지 않은 경우 생성 처리.
if( g_listenSocket == -1 )
{
// Listen Socket 생성 처리
if( MakeListenSocketV6( g_listenSocket ) == false )
{
// Listen socket 실패시
// - 지정된 시간 대기 후 다시 실행하도록 처리
// 지정된 시간동안 sleep 한 후 다시 check 수행
nanosleep( &sleepLoop, NULL );
continue;
}
}
// client accept 처리를 수행한다.
// - accept 는 blocking 함수 이므로..
// - client 가 없는 상황에서 blocking 되면... 서비스 상태를 바로 반영할 수 없는 문제가 발생
// - 따라서... select() 함수를 사용하여 timeout 처리를 수행한다.
selectTimeOut.tv_sec = ACCEPT_TIMEOUT_SEC;
selectTimeOut.tv_usec = 0;
FD_SET( g_listenSocket, &selectFds );
iSelectResult = select( g_listenSocket + 1, &selectFds, ( fd_set * ) NULL, ( fd_set * ) NULL, &selectTimeOut );
if( iSelectResult == 0 )
{
// Timeout 인 경우
continue;
}
else if( iSelectResult > 0 )
{
// accept 처리 수행
clientSocket = accept( g_listenSocket, ( struct sockaddr * ) &clientSockAddr, &clientSockLen );
if( clientSocket == -1 )
{
// 오류 발생시
errorNum = errno;
LOG( LERR, "[MonPortControl] client accept faild. [%d][%s]", errorNum, strerror( errorNum ) );
}
else
{
// client IP 주소 추출
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
if( inet_ntop( AF_INET6, &( clientSockAddr.sin6_addr ), tempBuffer, sizeof( tempBuffer ) ) == NULL )
{
errorNum = errno;
LOG( LERR, "[MonPortControl] client inet_ntop error. [%d][%s]", errorNum, strerror( errorNum ) );
sprintf( tempBuffer, "unknown" );
}
// 관련 내역 로깅 후 monitoring 처리 socket 이므로 client 종료 처리.
_LOG( LINF, "[MonPortControl] client accepted. ip: %s", tempBuffer );
close( clientSocket );
}
}
else // select 함수 오류
{
errorNum = errno;
LOG( LERR, "[MonPortControl] listen socket select func faild. [%d][%s]", errorNum, strerror( errorNum ) );
// select 함수 오류시 listen socket 문제인 경우이므로... listen socket close 처리한다.
CloseListenSocket( g_listenSocket );
}
}
// 종료시 정리 작업 수행
ReadyToExitWorkerMonPortControl();
return EXIT_SUCCESS;
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerMonPortControl()
{
// listen socket close
CloseListenSocket( g_listenSocket );
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker MonPortControl 프로세스에 대한 종료 처리 수신시.
static void SignalWorkerMonPortControlTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[MonPortControl:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[MonPortControl:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerMonPortControl();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::MonPortControl Process 의 signal 처리기...
void SetWorkerMonPortControlSignalHandler()
{
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 = SignalWorkerMonPortControlTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
@@ -0,0 +1,41 @@
/****************************************************************************
Worker::MonPortControl process main
-----------------------------------------
begin : 2018/02/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 __WORKER_MONITORING_PORT_CONTROL_H__
#define __WORKER_MONITORING_PORT_CONTROL_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::MonPortControl 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerMonPortControlMain( void );
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerMonPortControl( void );
// Worker::MonPortControl Process 의 signal 처리기...
void SetWorkerMonPortControlSignalHandler( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_MONITORING_PORT_CONTROL_H__ */
+381
View File
@@ -0,0 +1,381 @@
#include "WorkerSend.h"
#include "ProcessConfig.h"
#include "Logger.h"
#include "ShmDefine.h"
#include "FhsShm.h"
#include "RctsSocket.h"
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <time.h>
// 수집된 시스템 정보 및 서비스 상태 정보의 RCTS 전송 주기.
#define SEND_INTERVAL_SYSTEM_INFO 10
#define SEND_INTERVAL_SERVICE_STATUS 4
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main 프로세스에서 생성 처리하며... 전역변수로 설정되어 있다.
extern CFhsShm * g_pFhsShm;
// System 수집 정보의 RCTS 로 전송을 처리할 Thread 함수 선언. ( .h 에 선언시.. 다른 process 모듈에 포함되므로.. 이를 방지하기 위함)
void * WorkerSendSystemThread( void * arg );
// service 상태 정보의 RCTS 로 전송을 처리할 Thread 함수 선언. ( .h 에 선언시.. 다른 process 모듈에 포함되므로.. 이를 방지하기 위함)
void * WorkerSendServiceThread( void * arg );
// 생성된 각 Thread 의 초기화 실패 여부 flag
// - 초기화 실패시... 프로세스를 종료처리 하기 위함.
bool g_bThreadInitError = false;
// Worker::Send 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerSendMain()
{
setproctitle( "Send [initialize]" );
// Set Signal Handler
SetWorkerSendSignalHandler();
// Main Process 에서 생성한 공유메모리 접근 객체 유효성 확인
// NULL 인 경우... 초기화 실패로 처리한다.
if( g_pFhsShm == NULL )
{
// 객체 생성 실패시
LOG( LERR, "[Send:%d] Shared memory object not valid. process exit", getpid() );
ReadyToExitWorkerSend();
return EXIT_FAILURE;
}
// 전체 프로세스의 생성이 완료될때까지 대기 후 작업 시작
// - 시스템 정보 수집 및 서비스 체크가 1회 완료된 후 동작하도록 3 sec 간 대기 처리.
sleep( 3 );
setproctitle( "Send" );
// System 수집 정보의 RCTS 로 전송을 처리할 Thread 생성.
pthread_t threadWorkerSendSystem;
if( pthread_create( &threadWorkerSendSystem, NULL, WorkerSendSystemThread, NULL ) != 0 )
{
int errorNum = errno;
LOG( LERR, "[Send:%d] system info send thread create fail[%d][%s]. process exit", getpid(), errorNum, strerror( errorNum ) );
ReadyToExitWorkerSend();
return EXIT_FAILURE;
}
// service 상태 정보의 RCTS 로 전송을 처리할 Thread 생성.
pthread_t threadWorkerSendService;
if( pthread_create( &threadWorkerSendService, NULL, WorkerSendServiceThread, NULL ) != 0 )
{
int errorNum = errno;
LOG( LERR, "[Send:%d] service status send thread create fail[%d][%s]. process exit", getpid(), errorNum, strerror( errorNum ) );
ReadyToExitWorkerSend();
return EXIT_FAILURE;
}
// Thread 생성 및 초기화시 오류 발생 여부 확인.
// - Thread 생성 및 초기화가 완료될때까지 잠시 대기 후...
// - Thread 오류 발생시 프로세스 종료 처리.
for( int i = 0; i < 5; i++ )
{
sleep( 1 );
if( g_bThreadInitError == true )
{
LOG( LERR, "[Send:%d] send thread init fail. process exit", getpid());
ReadyToExitWorkerSend();
return EXIT_FAILURE;
}
}
// 대기
while( 1 )
{
// 할일 없어서.. 시그널 수신까지 대기 한다.
pause();
}
// 종료시 정리 작업 수행
ReadyToExitWorkerSend();
return EXIT_SUCCESS;
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerSend()
{
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker Send 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerSendTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[Send:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[Send:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerSend();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::Send Process 의 signal 처리기...
void SetWorkerSendSignalHandler()
{
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 = SignalWorkerSendTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
// System 수집 정보의 RCTS 로 전송을 처리할 Thread
void * WorkerSendSystemThread( void * arg )
{
// 메인 함수에서 이 스레드가 종료되기를 기다리지 않게 분리
pthread_detach( pthread_self() );
_LOG( LINF, "[Send:%d] system info send thread start.", getpid() );
// RCTS 와 통신을 담당할 객체 생성 및 초기화.
CRctsSocket rcts;
if( rcts.Init( CProcessConfig::GetInstance()->GetRctsServer(),
CProcessConfig::GetInstance()->GetRctsSystemInfoPort(),
CProcessConfig::GetInstance()->GetRcId() ) == false )
{
// RCTS 소켓 초기화 실패시..
// - 로깅 후..
// - 전역 flag 에 오류 설정.
// - thread 종료 처리.
LOG( LERR, "[Send:%d] system info send thread error. rcts socket init fail", getpid());
g_bThreadInitError = true;
// Thread 종료 관련 처리.
return NULL;
}
// 시스템 수집 정보를 저장할 변수.
struct SystemInfo stSystem;
// 마지막 시스템 정보 수집 시간 정보를 저장할 변수
time_t timePrevSystemCheck = 0;
time_t timeCurrentSystemCheck = 0;
// 초기 기동시... Service 상태 check 정보 전송 후... System 정보가 전송되도록
// 약 2 sec 대기 후 전송 시작
sleep( 2 );
// 공유메모리에 저장된 System 수집 정보를 RCTS 에 주기적으로 전송.
while( 1 )
{
// 2018-02-02 CHG huibong
// - system 정보 수집시 hang 등이 발생한 경우 RCTS 로 과거 정보 전송하지 않는다. (#31701)
// -- 즉 공유메모리 상의 last check 시간 정보를 기록했다가... 해당 정보가 바뀐 경우에만 RCTS 로 전송 처리
timeCurrentSystemCheck = g_pFhsShm->GetTimeSystemCheck();
if( timeCurrentSystemCheck == 0 )
{
// 아직 초기화 중인 상태
// - 전송하지 말고.. 1 sec 정도 대기 후 다시 시도
sleep( 1 );
continue;
}
// 현재 시간 정보와 이전 시간 정보가 동일한 경우.. 전송하지 말고
if( timeCurrentSystemCheck == timePrevSystemCheck )
{
_LOG( LERR, "[RCTS send system] system collect time not changed. prev[%ld] current[%ld]. RCTS not send."
, timePrevSystemCheck, timeCurrentSystemCheck );
// 2 sec 대기 후 재시도
sleep( 2 );
continue;
}
else
{
timePrevSystemCheck = timeCurrentSystemCheck;
}
// 임시변수 초기화.
memset( &stSystem, 0x00, sizeof( struct SystemInfo ) );
// 공유메모리에 저장된 시스템 수집 정보 복사.
memcpy( &stSystem, g_pFhsShm->GetSystemInfo(), sizeof( struct SystemInfo ) );
//_LOG( LINF, "[Send:%d] system info send: mem_usage[%d] storage_avail[%ju] tcp80_est[%d]"
// , getpid(), stSystem.memory_usage, stSystem.storage_avail, stSystem.tcp_80_establish );
// System 수집 정보를 RCTS 로 전송.
// - 실패 관련 로깅은 전송 모듈에서 처리.
// - 실패했다고 해서 딱히 할일 없으므로.. 반환값 처리 안함.
rcts.SendSystemInfo( &stSystem);
// 지정된 주기마다 전송 처리.
sleep( SEND_INTERVAL_SYSTEM_INFO );
}
// Thread 종료 관련 처리.
rcts.Close();
return NULL;
}
// service 상태 정보의 RCTS 로 전송을 처리할 Thread
void * WorkerSendServiceThread( void * arg )
{
// 메인 함수에서 이 스레드가 종료되기를 기다리지 않게 분리
pthread_detach( pthread_self() );
_LOG( LINF, "[Send:%d] service status send thread start.", getpid() );
// RCTS 와 통신을 담당할 객체 생성 및 초기화.
CRctsSocket rcts;
if( rcts.Init( CProcessConfig::GetInstance()->GetRctsServer(),
CProcessConfig::GetInstance()->GetRctsServiceInfoPort(),
CProcessConfig::GetInstance()->GetRcId() ) == false )
{
// RCTS 소켓 초기화 실패시..
// - 로깅 후..
// - 전역 flag 에 오류 설정.
// - thread 종료 처리.
LOG( LERR, "[Send:%d] service status send thread error. rcts socket init fail", getpid() );
g_bThreadInitError = true;
// Thread 종료 관련 처리.
return NULL;
}
// Hostname 정보를 저장할 변수.
std::string strHostname;
// 서비스 상태 정보를 저장할 변수.
struct ServiceStatus stService;
// Disk 최대 사용률 정보를 저장할 변수.
int iStorageMaxUsage;
// 공유메모리에 저장된 Service Check start time 정보를 저장할 변수
time_t timeServiceCheck = 0;
struct timespec currentClock;
// 공유메모리에 저장된 Service 상태 정보를 RCTS 에 주기적으로 전송.
while( 1 )
{
// 2018-02-05 NEW huibong service check 관련 last check 시작 시간 timeout 기능 추가 (#31701)
// - service check 시작시 공유메모리에 시간 정보 기록
// - service check Hang 발생으로 인한 timeout 발생시 서비스 상태를 Disable 상태로 변경하여 전송
timeServiceCheck = g_pFhsShm->GetTimeServiceCheck();
if( timeServiceCheck == 0 )
{
// 아직 초기화 중인 상태
// - 전송하지 말고.. 1 sec 정도 대기 후 다시 시도
sleep( 1 );
continue;
}
else
{
// 그 외의 경우는 현재 시간을 추출하여 timeout 발생 여부 check 한다.
memset( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
if( timeServiceCheck < currentClock.tv_sec - TIMEOUT_SERVICE_CHECK )
{
// timeout 발생시 서비스 Disable 상태로 변경 처리
g_pFhsShm->SetServiceStat( SERVICE_STAT_DISABLE );
_LOG( LERR, "[RCTS send service] service check timeout[%d] last[%ld], current[%ld]. service stat set: %d (%s)"
, TIMEOUT_SERVICE_CHECK, timeServiceCheck, currentClock.tv_sec
, g_pFhsShm->GetServiceStat(), g_pFhsShm->GetServiceStatString() );
}
}
// 임시변수 초기화.
memset( &stService, 0x00, sizeof( struct ServiceStatus ) );
iStorageMaxUsage = 0;
strHostname.clear();
// hostname 정보 저장.
strHostname = g_pFhsShm->GetHostName();
// 공유메모리에 저장된 서비스 상태 관련 정보 복사.
memcpy( &stService, g_pFhsShm->GetServiceStatus(), sizeof( struct ServiceStatus ) );
// 공유메모리에 저장된 Storage 최대 사용률 정보 복사.
iStorageMaxUsage = g_pFhsShm->GetStorageMaxUsage();
//_LOG( LINF, "[Send:%d] service status send: hostname[%s] service[%d] max_usage[%d]"
// , getpid(), strHostname.c_str(), stService.stat, iStorageMaxUsage );
// Service 상태 정보를 RCTS 로 전송.
// - 실패 관련 로깅은 전송 모듈에서 처리.
// - 실패했다고 해서 딱히 할일 없으므로.. 반환값 처리 안함.
rcts.SendServiceStatus( strHostname, &stService, iStorageMaxUsage );
// 지정된 주기마다 전송 처리.
sleep( SEND_INTERVAL_SERVICE_STATUS );
}
// Thread 종료 관련 처리.
rcts.Close();
return NULL;
}
+41
View File
@@ -0,0 +1,41 @@
/****************************************************************************
Worker::Send process main
-----------------------------------------
begin : 2015/04/22
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __WORKER_SEND_H__
#define __WORKER_SEND_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::Send 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerSendMain( void );
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerSend( void );
// Worker::Send Process 의 signal 처리기...
void SetWorkerSendSignalHandler( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_SEND_H__ */
+151
View File
@@ -0,0 +1,151 @@
#include "WorkerServiceCheck.h"
#include "ProcessConfig.h"
#include "Logger.h"
#include "FhsShm.h"
#include "ServiceCheck.h"
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <time.h>
// 서비스 Check 주기 (sec)
#define SERVICE_CHECK_INTERVAL 4
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main 프로세스에서 생성 처리하며... 전역변수로 설정되어 있다.
extern CFhsShm * g_pFhsShm;
// Worker::ServiceCheck 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerServiceCheckMain()
{
setproctitle( "ServiceCheck [initialize]" );
// Set Signal Handler
SetWorkerServiceCheckSignalHandler();
// Main Process 에서 생성한 공유메모리 접근 객체 유효성 확인
// NULL 인 경우... 초기화 실패로 처리한다.
if( g_pFhsShm == NULL )
{
// 객체 생성 실패시
LOG( LERR, "[ServiceCheck:%d] Shared memory object not valid. process exit", getpid() );
ReadyToExitWorkerServiceCheck();
return EXIT_FAILURE;
}
// Service 상태 Check를 담당할 객체 생성 및 초기화.
CServiceCheck serviceCheck( g_pFhsShm );
if( serviceCheck.Init( CProcessConfig::GetInstance()->GetSvcNic()
, CProcessConfig::GetInstance()->GetCheckDns()
, CProcessConfig::GetInstance()->GetFimngdCheckPort() ) == false )
{
// 객체 초기화 실패시
LOG( LERR, "[ServiceCheck:%d] service check module init fail. process exit", getpid() );
ReadyToExitWorkerServiceCheck();
return EXIT_FAILURE;
}
// 전체 프로세스의 생성이 완료될때까지 대기 후 작업 시작
sleep( 1 );
setproctitle( "ServiceCheck" );
// 최초 기동시 서비스 상태는 수동 중지(1) 상태로 설정한다.
// - Main 프로세스에서 공유메모리 초기화 작업시 처리함.
// g_pFhsShm->SetServiceStat( SERVICE_STAT_OUT );
// 주기적으로 Service 상태를 점검 처리한다.
while( 1 )
{
// Service Check Start time 정보를 공유 메모리에 업데이트 (#31701)
// - Check 시 Hang 발생을 탐지할 수 있도록 expire 처리를 위한 시간값 업데이트 기능 추가
g_pFhsShm->UpdateTimeServiceCheck();
// Service 상태 Check 수행.
serviceCheck.Check();
// 지정된 시간동안 sleep
sleep( SERVICE_CHECK_INTERVAL );
}
// 종료시 정리 작업 수행
ReadyToExitWorkerServiceCheck();
return EXIT_SUCCESS;
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerServiceCheck()
{
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker ServiceCheck 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerServiceCheckTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[ServiceCheck:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[ServiceCheck:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerServiceCheck();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::ServiceCheck Process 의 signal 처리기...
void SetWorkerServiceCheckSignalHandler()
{
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 = SignalWorkerServiceCheckTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
+42
View File
@@ -0,0 +1,42 @@
/****************************************************************************
Worker::ServiceCheck process main
-----------------------------------------
begin : 2015/04/22
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __WORKER_SERVICE_CHECK_H__
#define __WORKER_SERVICE_CHECK_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::ServiceCheck 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerServiceCheckMain( void );
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerServiceCheck( void );
// Worker::ServiceCheck Process 의 signal 처리기...
void SetWorkerServiceCheckSignalHandler( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_SERVICE_CHECK_H__ */
+156
View File
@@ -0,0 +1,156 @@
#include "WorkerStorageCheck.h"
#include "ProcessConfig.h"
#include "Logger.h"
#include "FhsShm.h"
#include "StorageCheck.h"
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <time.h>
// Storage status Check 주기 (sec)
// - service check 모듈에서 30 sec timeout 처리
// - 1회 Check 당 sleep 등으로 인해 2 sec 미만 시간 소요
// - 따라서 8 sec 마다 check 하도록 설정함.
#define STORAGE_CHECK_INTERVAL 8
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main 프로세스에서 생성 처리하며... 전역변수로 설정되어 있다.
extern CFhsShm * g_pFhsShm;
// Worker::StorageCheck 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerStorageCheckMain()
{
setproctitle( "StorageCheck [initialize]" );
// Set Signal Handler
SetWorkerStorageCheckSignalHandler();
// Main Process 에서 생성한 공유메모리 접근 객체 유효성 확인
// NULL 인 경우... 초기화 실패로 처리한다.
if( g_pFhsShm == NULL )
{
// 객체 생성 실패시
LOG( LERR, "[StorageCheck:%d] Shared memory object not valid. process exit", getpid() );
ReadyToExitWorkerStorageCheck();
return EXIT_FAILURE;
}
// Storage status check 작업을 수행할 객체 생성 및 초기화
CStorageCheck storageCheck( g_pFhsShm );
if( storageCheck.Init( CProcessConfig::GetInstance()->GetStorageRoot() ) == false )
{
// 객체 생성 실패시
LOG( LERR, "[StorageCheck:%d] storage check module init fail. process exit", getpid() );
ReadyToExitWorkerStorageCheck();
return EXIT_FAILURE;
}
// 전체 프로세스의 생성이 완료될때까지 대기 후 작업 시작
sleep( 1 );
setproctitle( "StorageCheck" );
struct timespec sleepLoop;
sleepLoop.tv_sec = STORAGE_CHECK_INTERVAL;
sleepLoop.tv_nsec = 0;
// 실제 Storage Check 수행 전 공유메모리 값에 현재 시간 정보 기록 처리
// - timeout 처리시 사용할 수 있도록 초기화 시키기 위함
g_pFhsShm->UpdateTimeStorageCheck( true );
// Check 결과 정보 저장을 위한 변수.
bool bCheckResult;
// 주기적으로 Storage 상태를 점검 처리한다.
while( 1 )
{
// Storage 상태 Check 수행.
bCheckResult = storageCheck.Check( CProcessConfig::GetInstance()->GetStorageRoot() );
// Check 결과에 따른 공유메모리 업데이트 작업 수행
// - 위의 check() 함수가 hang 걸릴 경우 아래 시간 업데이트 못하므로.. expire 기능 필요.
g_pFhsShm->UpdateTimeStorageCheck( bCheckResult );
// 지정된 시간동안 sleep
nanosleep( &sleepLoop, NULL );
}
// 종료시 정리 작업 수행
ReadyToExitWorkerStorageCheck();
return EXIT_SUCCESS;
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerStorageCheck()
{
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker StorageCheck 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerStorageCheckTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[StorageCheck:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[StorageCheck:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerStorageCheck();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::StorageCheck Process 의 signal 처리기...
void SetWorkerStorageCheckSignalHandler()
{
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 = SignalWorkerStorageCheckTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
+41
View File
@@ -0,0 +1,41 @@
/****************************************************************************
Worker::StorageCheck process main
-----------------------------------------
begin : 2018/01/29
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_STORAGE_CHECK_H__
#define __WORKER_STORAGE_CHECK_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::StorageCheck 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerStorageCheckMain( void );
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerStorageCheck( void );
// Worker::StorageCheck Process 의 signal 처리기...
void SetWorkerStorageCheckSignalHandler( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_STORAGE_CHECK_H__ */
+237
View File
@@ -0,0 +1,237 @@
#include "WorkerSystemCollect.h"
#include "ProcessConfig.h"
#include "Logger.h"
#include "FhsShm.h"
#include "SystemInfo.h"
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <time.h>
// 시스템 정보 추출 주기 (sec)
#define SYSTEM_COLLECT_INTERVAL 10
// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main 프로세스에서 생성 처리하며... 전역변수로 설정되어 있다.
extern CFhsShm * g_pFhsShm;
// Worker::SystemCollect 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerSystemCollectMain()
{
setproctitle( "SystemCollect [initialize]" );
// Set Signal Handler
SetWorkerSystemCollectSignalHandler();
// Main Process 에서 생성한 공유메모리 접근 객체 유효성 확인
// NULL 인 경우... 초기화 실패로 처리한다.
if( g_pFhsShm == NULL )
{
// 객체 생성 실패시
LOG( LERR, "[SystemCollect:%d] Shared memory object not valid. process exit", getpid() );
ReadyToExitWorkerSystemCollect();
return EXIT_FAILURE;
}
// system info 수집을 담당할 객체 생성 및 초기화.
CSystemInfo systemInfo( g_pFhsShm );
if( systemInfo.Init() == false )
{
// 객체 초기화 실패시
LOG( LERR, "[SystemCollect:%d] system info collect module init fail. process exit", getpid() );
ReadyToExitWorkerSystemCollect();
return EXIT_FAILURE;
}
// 전체 프로세스의 생성이 완료될때까지 대기 후 작업 시작
sleep( 1 );
setproctitle( "SystemCollect" );
// 최초 기동인지 여부
bool bFirstLoop = true;
struct timespec timeStart; // 소요시간 측정을 위한 멤버변수
struct timespec timeEnd; // 소요시간 측정을 위한 멤버변수
double dElapsedTime; // 소요시간 측정을 위한 멤버변수
// 주기적으로 시스템 정보를 추출한다.
while( 1 )
{
// check start time
clock_gettime( CLOCK_MONOTONIC, &timeStart );
// System Check Start time 정보를 공유 메모리에 업데이트 (#31701)
// - 정보 수집 Hang 발생을 탐지할 수 있도록 expire 처리를 위한 시간값 업데이트 기능 추가
g_pFhsShm->UpdateTimeSystemCheck();
// hostname
// - 추출 실패시.. 업무 규약에 따라 프로세스 종료 처리
// - shared memory 설정 실패시.. 업무 규약에 따라 프로세스 종료 처리
if( systemInfo.GetHostname() == false )
{
LOG( LERR, "[System] hostname collect fail. => Process exit" );
ReadyToExitWorkerSystemCollect();
return EXIT_FAILURE;
}
_LOG( LINF, "[System] hostname collect passed." );
// load average
systemInfo.GetLoadAverage();
_LOG( LINF, "[System] load average collect passed." );
// total memory
// - 최초 loop 시에만 정보 수집 ( 오류발생시 프로세스 종료 처리)
if( bFirstLoop == true )
{
if( systemInfo.GetMemoryTotal() == false )
{
LOG( LERR, "[System] total memory collect fail. => Process exit" );
ReadyToExitWorkerSystemCollect();
return EXIT_FAILURE;
}
_LOG( LINF, "[System] total memory collect passed." );
}
// free memory
systemInfo.GetMemoryFree();
_LOG( LINF, "[System] free memory collect passed." );
// swap
systemInfo.GetSwap();
_LOG( LINF, "[System] swap memory collect passed." );
// mount
systemInfo.GetMount( CProcessConfig::GetInstance()->GetStorageRoot() );
_LOG( LINF, "[System] storage mount & size info collect passed." );
// disk status
// - queue length, busy
systemInfo.GetDiskStatus();
_LOG( LINF, "[System] disk busy info collect passed." );
// network status
systemInfo.GetNetworkStatus();
_LOG( LINF, "[System] network status collect passed." );
// tcp session count
systemInfo.GetTcpSession();
_LOG( LINF, "[System] tcp session collect passed." );
// Debug 인 경우에만.. 로그상에 shared memory 내역 출력 처리
//if( CLogger::GetInstance()->GetLogLevel() == LDBG )
// g_pFhsShm->Print();
/////////////////////////////////////
// check end time
clock_gettime( CLOCK_MONOTONIC, &timeEnd );
// 작업 소요 시간 계산
dElapsedTime = timeEnd.tv_sec - timeStart.tv_sec;
dElapsedTime += ( timeEnd.tv_nsec - timeStart.tv_nsec ) * 1e-9;
_LOG( LINF, "[System] collect end. total elapsed time: %.3lf sec.", dElapsedTime );
// 공유메모리 설정 내역을 로그 파일에 기록 처리한다.
// - 본 루프가 SYSTEM_COLLECT_INTERVAL 간격으로 동작하므로.. 로깅 처리하기에 가장 적당하다고 판단됨.
g_pFhsShm->Print();
// loop 1회 실행했으므로.. 최초 loop 플래그를 false 로 변경.
if( bFirstLoop == true )
bFirstLoop = false;
// 지정된 시간동안 sleep
sleep( SYSTEM_COLLECT_INTERVAL );
}
// 종료시 정리 작업 수행
ReadyToExitWorkerSystemCollect();
return EXIT_SUCCESS;
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerSystemCollect()
{
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker SystemCollect 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerSystemCollectTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[SystemCollect:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[SystemCollect:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerSystemCollect();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::SystemCollect Process 의 signal 처리기...
void SetWorkerSystemCollectSignalHandler()
{
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 = SignalWorkerSystemCollectTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
+42
View File
@@ -0,0 +1,42 @@
/****************************************************************************
Worker::SystemCollect process main
-----------------------------------------
begin : 2015/04/22
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __WORKER_SYSTEM_COLLECT_H__
#define __WORKER_SYSTEM_COLLECT_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::SystemCollect 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerSystemCollectMain( void );
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerSystemCollect( void );
// Worker::SystemCollect Process 의 signal 처리기...
void SetWorkerSystemCollectSignalHandler( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_SYSTEM_COLLECT_H__ */
+225
View File
@@ -0,0 +1,225 @@
#include "WorkerTelegrafControl.h"
#include "Logger.h"
#include "TelegrafControl.h"
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <syslog.h>
// Telegraf check 주기 (sec)
#define TELEGRAF_CHECK_INTERVAL 60
// telegraf 미기동 상태가 지정된 횟수 이상인 경우.. 기동 처리 수행
// - 운영자 요청으로 10분 초과 미기동 상태인 경우.. 자동 기동 처리
#define TELEGRAF_NO_ACTIVE_COUNT_MAX 10
// telegraf 가 hang 상태 관련 지정된 횟수 이상인 경우.. 재기동 처리 수행
#define TELEGRAF_HANG_COUNT_MAX 2
// telegraf 의 MAX 기동 시간 (sec)
// - telegraf 기동 후 elapsed time 이 현재 설정값을 초과하는 경우.. 재기동 처리를 수행한다.
// - 이는 telegraf 장기 운영시.. 삭제된 log 파일을 tail 에서 붙잡고 있어.... OS Disk full 상황을 방지하기 위함
// - 운영자 의견 수렴 결과.. 2일 단위로 재기동 요청.
#define TELEGRAF_ELAPSED_TIME_MAX 172800LL
// Worker::TelegrafControl 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerTelegrafControlMain( void )
{
setproctitle( "TelegrafControl [initialize]" );
// Set signal handler
SetWorkerTelegrafControlSignalHandler();
// telegraf control 객체 생성
CTelegrafControl telegraf;
// 전체 프로세스의 생성이 완료될때까지 대기 후 작업 시작
sleep( 2 );
setproctitle( "TelegrafControl" );
int iProcessId = 0;
int iProcessNotActiveCount = 0;
int iProcessHangCount = 0;
long long llProcessElapsedSec = 0;
// 주기적으로 telegraf 상태를 점검한다.
while( 1 )
{
// 지정된 시간동안 sleep 처리
sleep( TELEGRAF_CHECK_INTERVAL );
_LOG( LDBG, "[TelegrafControl:%d] check start.", getpid() );
// 1. process 기동 여부 check
iProcessId = telegraf.GetProcessId();
if( iProcessId == 0 )
{
// 미기동 상태인 경우
// - 패치 및 설정 등의 작업으로 인해 미기동 상태일 수 있으므로......
// - 미기동 상태가 지정된 횟수 이상인 경우에만.. 기동 처리를 시도한다.
++iProcessNotActiveCount;
if( iProcessNotActiveCount > TELEGRAF_NO_ACTIVE_COUNT_MAX )
{
_LOG( LINF, "[TelegrafControl:%d] telegraf process not found. try starting.", getpid() );
// 지정 횟수 초과의 미기동 상태인 경우... 기동 처리 try...
telegraf.ServiceStart();
// telegraf 기동을 시도했으므로... count 는 초기화 처리한다.
iProcessNotActiveCount = 0;
}
else
{
_LOG( LINF, "[TelegrafControl:%d] telegraf process not found. count[%d]", getpid(), iProcessNotActiveCount );
}
// telegraf 상태 처음부터 다시 check.
continue;
}
// telegraf 기동 중인 상태인 경우 not active count 초기화
iProcessNotActiveCount = 0;
// 2. process hang check
// - hang 상황인 경우... 재기동 처리
if( telegraf.IsHang() == true )
{
// telegraf hang 상황인 경우...
++iProcessHangCount;
if( iProcessHangCount > TELEGRAF_HANG_COUNT_MAX )
{
_LOG( LINF, "[TelegrafControl:%d] telegraf[%d] hang detected. try restarting.", getpid(), iProcessId );
// 지정 횟수 초과의 hang 상태인 경우... 재기동 처리 try...
telegraf.ServiceQuit();
// telegraf 재기동을 시도했으므로... count 는 초기화 처리한다.
iProcessHangCount = 0;
// 관련 사항은 /var/log/message 상에 기록해 둔다.
openlog( PROG_NAME, LOG_PID, LOG_USER );
syslog( LOG_WARNING, "telegraf[%d] hang detected. SIGQUIT send for telegraf restart.", iProcessId );
closelog();
}
else
{
_LOG( LINF, "[TelegrafControl:%d] telegraf[%d] is hang. count[%d]", getpid(), iProcessId, iProcessHangCount );
}
// telegraf 상태 처음부터 다시 check.
continue;
}
// telegraf hang count 초기화
iProcessHangCount = 0;
// 3. process 기동 후 지정된 시간 이상 되었는지 check
// - 지정된 시간 초과시 재기동 처리
// - ps -o etimes= -p `pgrep telegraf`
// - sec 단위
llProcessElapsedSec = telegraf.GetProcessElapsedSec();
if( llProcessElapsedSec > TELEGRAF_ELAPSED_TIME_MAX )
{
_LOG( LINF, "[TelegrafControl:%d] telegraf[%d] elapsed time too long [%lld / %lld sec]. try restarting."
, getpid(), iProcessId, llProcessElapsedSec, TELEGRAF_ELAPSED_TIME_MAX );
// 재기동 처리 시도
telegraf.ServiceRestart();
// telegraf 상태 처음부터 다시 check.
continue;
}
// 4. 기타
}
// 종료시 정리 작업 수행
ReadyToExitWorkerTelegrafControl();
return EXIT_SUCCESS;
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerTelegrafControl( void )
{
// Logger 객체 종료 처리
CLogger::Exit();
return;
}
// Worker TelegrafControl 프로세스에 대한 종료 처리 수신시
static void SignalWorkerTelegrafControlTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[TelegrafControl:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[TelegrafControl:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerTelegrafControl();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::TelegrafControl Process 의 signal 처리기...
void SetWorkerTelegrafControlSignalHandler( void )
{
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 = SignalWorkerTelegrafControlTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
+42
View File
@@ -0,0 +1,42 @@
/****************************************************************************
Worker::TelegrafControl process main
-----------------------------------------
begin : 2021/05/07
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __WORKER_TELEGRAF_CONTROL_H__
#define __WORKER_TELEGRAF_CONTROL_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::TelegrafControl 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerTelegrafControlMain( void );
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerTelegrafControl( void );
// Worker::TelegrafControl Process 의 signal 처리기...
void SetWorkerTelegrafControlSignalHandler( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_TELEGRAF_CONTROL_H__ */
+5
View File
@@ -0,0 +1,5 @@
// sample 소스 파일
#include ""
+32
View File
@@ -0,0 +1,32 @@
/****************************************************************************
rc_apid header
-----------------------------------------
begin : 2021/03/09
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __APID_DATA_H__
#define __APID_DATA_H__
#include <>
// class 설명
class C
{
};
#endif /* __APID_DATA_H__ */
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
#
# Description : process compile test by huibong
#
PROG_NAME=csagentd
killall $PROG_NAME
gmake clean
killall $PROG_NAME
gmake
sleep 1
#./$PROG_NAME -c ../fhs.conf
./$PROG_NAME
sleep 1
ps aux | grep $PROG_NAME | grep -v grep | grep -v tail
+65
View File
@@ -0,0 +1,65 @@
#****************************************************************************
# Makefile for Cloud Storage test code
# -----------------------------------------
#
# begin : 2015/04/13
# copyright : (C) 2013 Solbox Inc.
# author : Dev Storage Team
# email : storage.sd@solbox.com
# version : 3.5.0
#
# CopyRight(C) 2005 Solbox Inc. All Rights reserved.
# Redistribution and use in source and binary forms, with or with out
# modification, are not permitted in outside of Solbox Inc.
#****************************************************************************
# Program info
APP_SYSTEM = system_test
OBJ_SYSTEM = system_test.o
APP_SERVICE = service_check
OBJ_SERVICE = service_check.o
APP = $(APP_SYSTEM) $(APP_SERVICE)
# Compiler info
CC = /usr/bin/gcc
#CC = /usr/bin/g++
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized\
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual -fno-rtti\
-D_REENTRANT
############
all:$(APP)
sync
%.o: %.cpp
$(CC) $(CPLAGS) -o $@ -c $^
$(APP_SYSTEM) : $(OBJ_SYSTEM)
$(CC) -o $@ $(OBJ_SYSTEM) -lkvm -lgeom -ldevstat
$(APP_SERVICE) : $(OBJ_SERVICE)
$(CC) -o $@ $(OBJ_SERVICE)
clean :
-rm -f *.o core *.out *.log
-rm -f $(OBJ_SYSTEM) $(OBJ_SERVICE)
-rm -f $(APP)
sync
install :
sync
new :
$(MAKE) clean; $(MAKE); sync
# End of Makefile
+12
View File
@@ -0,0 +1,12 @@
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("\nService check test....\n");
return 0;
}
+937
View File
@@ -0,0 +1,937 @@
#include <sys/types.h>
#include <sys/param.h>
#include <stddef.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <stdbool.h>
#include <sys/socket.h>
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif
#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var))
struct network_info {
struct timespec check_time;
u_long inbound_packets;
u_long outbound_packets;
u_long inbound_bytes;
u_long outbound_bytes;
};
struct fhs_info {
char hostname[HOST_NAME_MAX + 1];
double load_avg[3];
float cpu_stat[CPUSTATES];
int memory_total; // MByte
int memory_stat[6]; // MByte
int swap_stat[6]; // Size MByte, in/out : KByte
struct network_info network_old;
struct network_info network_now;
};
static void getsysctl(const char *name, void *ptr, size_t len)
{
size_t nlen = len;
if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1)
{
fprintf(stderr, "sysctl(%s...) failed: %s\n", name, strerror(errno));
//quit(23);
}
if (nlen != len)
{
fprintf(stderr, "sysctl(%s...) expected %lu byte, got %lu byte\n", name, (unsigned long)len, (unsigned long)nlen);
//quit(23);
}
return;
}
long percentages(int cnt, int * out, register long * new, register long * old, long * diffs)
{
register int i;
register long change;
register long total_change;
register long *dp;
long half_total;
/* initialization */
total_change = 0;
dp = diffs;
/* calculate changes for each state and the overall change */
for (i = 0; i < cnt; i++)
{
if ((change = *new - *old) < 0)
{
/* this only happens when the counter wraps */
change = (int)
((unsigned long)*new-(unsigned long)*old);
}
total_change += (*dp++ = change);
*old++ = *new++;
}
/* avoid divide by zero potential */
if (total_change == 0)
{
total_change = 1;
}
/* calculate percentages based on overall change, rounding up */
half_total = total_change / 2l;
/* Do not divide by 0. Causes Floating point exception */
if(total_change) {
for (i = 0; i < cnt; i++)
{
*out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
}
}
/* return the total in case the caller wants to use it */
return(total_change);
}
bool get_loadavg( struct fhs_info * fhs)
{
int i = 0;
struct loadavg sysload;
GETSYSCTL("vm.loadavg", sysload);
//getsysctl("vm.loadavg", &sysload, sizeof(sysload));
for (i = 0; i < 3; i++)
{
fhs->load_avg[i] = (double)sysload.ldavg[i] / sysload.fscale;
}
return true;
}
static long cp_time[CPUSTATES];
static long cp_old[CPUSTATES];
static long cp_diff[CPUSTATES];
bool get_cpuusage( struct fhs_info * fhs )
{
int i;
int cpu_stat[CPUSTATES];
GETSYSCTL("kern.cp_time", cp_time);
percentages(CPUSTATES, cpu_stat, cp_time, cp_old, cp_diff);
for( i = 0; i < CPUSTATES; i++)
{
if( cpu_stat[i] >= 1000)
fhs->cpu_stat[i] = 100.0;
else
fhs->cpu_stat[i] = ((float)cpu_stat[i])/10.0;
}
return true;
}
static int pageshift; /* log base 2 of the pagesize */
#define pagetok(size) ((size) << pageshift)
bool get_memory( struct fhs_info * fhs )
{
long memory_total;
int memory_stat[6];
long bufspace = 0;
GETSYSCTL("hw.physmem", memory_total);
GETSYSCTL("vfs.bufspace", bufspace);
GETSYSCTL("vm.stats.vm.v_active_count", memory_stat[0]);
GETSYSCTL("vm.stats.vm.v_inactive_count", memory_stat[1]);
GETSYSCTL("vm.stats.vm.v_wire_count", memory_stat[2]);
GETSYSCTL("vm.stats.vm.v_cache_count", memory_stat[3]);
GETSYSCTL("vm.stats.vm.v_free_count", memory_stat[5]);
// byte -> Mbyte
fhs->memory_total = memory_total /1024/1024;
/* convert memory stats to Kbytes -> Mbytes: +512/1024 -> round() */
fhs->memory_stat[0] = ( pagetok(memory_stat[0]) + 512 ) /1024;
fhs->memory_stat[1] = ( pagetok(memory_stat[1]) + 512 ) /1024;
fhs->memory_stat[2] = ( pagetok(memory_stat[2]) + 512 ) /1024;
fhs->memory_stat[3] = ( pagetok(memory_stat[3]) + 512 ) /1024;
fhs->memory_stat[4] = (( bufspace / 1024) + 512 ) /1024;
fhs->memory_stat[5] = ( pagetok(memory_stat[5]) + 512 ) /1024;
return true;;
}
#include <fcntl.h>
#include <kvm.h>
static kvm_t *kd;
static int swapmode(int *retavail, int *retfree)
{
int n;
int pagesize = getpagesize();
struct kvm_swap swapary[1];
*retavail = 0;
*retfree = 0;
#define CONVERT(v) ((quad_t)(v) * pagesize / 1024)
n = kvm_getswapinfo(kd, swapary, 1, 0);
if (n < 0 || swapary[0].ksw_total == 0)
return (0);
*retavail = CONVERT(swapary[0].ksw_total);
*retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used);
n = (int)(swapary[0].ksw_used * 100.0 / swapary[0].ksw_total);
return (n);
}
static int swappgsin = -1;
static int swappgsout = -1;
static unsigned int swap_delay = 0;
static int swapavail = 0;
static int swapfree = 0;
bool get_swap(struct fhs_info * fhs)
{
int nspgsin, nspgsout;
GETSYSCTL("vm.stats.vm.v_swappgsin", nspgsin);
GETSYSCTL("vm.stats.vm.v_swappgsout", nspgsout);
// first interval
if (swappgsin < 0)
{
fhs->swap_stat[4] = 0;
fhs->swap_stat[5] = 0;
}
// compute differences between old and new swap statistic
else
{
fhs->swap_stat[4] = pagetok(((nspgsin - swappgsin)));
fhs->swap_stat[5] = pagetok(((nspgsout - swappgsout)));
}
swappgsin = nspgsin;
swappgsout = nspgsout;
// call CPU heavy swapmode() only for changes
if (fhs->swap_stat[4] > 0 || fhs->swap_stat[5] > 0 || swap_delay == 0)
{
fhs->swap_stat[3] = swapmode(&swapavail, &swapfree);
fhs->swap_stat[0] = ( swapavail + 512 ) / 1024;
fhs->swap_stat[1] = ( swapavail - swapfree + 512) / 1024;
fhs->swap_stat[2] = ( swapfree + 512 ) / 1024;
}
else
{
fhs->swap_stat[0] = ( swapavail + 512 ) / 1024;
fhs->swap_stat[1] = ( swapavail - swapfree + 512 ) / 1024;
fhs->swap_stat[2] = ( swapfree + 512 ) / 1024;
}
swap_delay = 1;
return true;
}
bool get_hostname( struct fhs_info * fi )
{
if( gethostname( fi->hostname, HOST_NAME_MAX ) == 0 )
return true;
else
{
printf("hostname get failed. [%d][%s]", errno, strerror(errno));
return false;
}
}
#include <net/if.h>
#include <net/if_mib.h>
#include <time.h>
int if_count[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_SYSTEM, IFMIB_IFCOUNT };
int if_info[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, 0, IFDATA_GENERAL };
bool get_network( struct fhs_info * fhs )
{
// 기존 추출된 정보를 old 로 이동 처리후 0 reset
memcpy( &(fhs->network_old), &(fhs->network_now), sizeof( struct network_info));
memset( &(fhs->network_now), 0x00, sizeof(struct network_info));
u_int count = 0;
size_t data_len = sizeof(count);
if( sysctl( if_count, 5, (void *)&count, (size_t *)&data_len, (void *)NULL, (size_t)0 ) != 0 )
{
printf("Network interface count get failed. [%d][%s]", errno, strerror(errno));
return false;
}
// interface 가 없는 경우
if( count <= 0 )
return true;
int i;
struct ifmibdata data;
data_len = sizeof( struct ifmibdata );
struct network_info total;
memset( &total, 0x00, sizeof(struct network_info));
for( i = 1; i <= count ; i++ )
{
if_info[4] = i;
if( (sysctl( if_info, 6, (void *)&data, (size_t *)&data_len, (void *)NULL, (size_t)0 ) != 0) && (errno != ENOENT) )
{
printf("Network interface sysctl error getting interface data. [%d][%s]", errno, strerror(errno));
return false;
}
// local loopback interface (lo) 제외
// - 장비의 트래픽 정보를 수집하는 것이므로.. lo 꺼는 빼는 것이 맞다.
if( strncmp( data.ifmd_name, "lo", 2) == 0 )
continue;
// link aggregation 처리되 lagg 쨒nterface 제외
// - link ggreagtion 처리된 개별 interface 통계가 lagg 에 sum 되어 나타나므로... 통계 중복 발생.
// - 이를 해결하기 위해 lagg interface 는 수집 제외
if( strncmp( data.ifmd_name, "lagg", 4) == 0 )
continue;
//printf("Network interface name : %s\n", data.ifmd_name );
total.inbound_packets += data.ifmd_data.ifi_ipackets;
total.outbound_packets += data.ifmd_data.ifi_opackets;
total.inbound_bytes += data.ifmd_data.ifi_ibytes;
total.outbound_bytes += data.ifmd_data.ifi_obytes;
}
// 시간 정보 추출
clock_gettime( CLOCK_MONOTONIC, &(total.check_time));
memcpy( &(fhs->network_now), &total, sizeof( struct network_info));
return true;
}
#include <sys/mount.h>
#include <stdint.h>
void print_mountinfo()
{
int mntsize;
struct statfs *mntbuf;
long blocksize;
int i;
getbsize( &mntsize, &blocksize);
//printf("\nblocksize = %ld\n", blocksize);
mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
printf("\nMount: /stg\n");
printf(" Filesystem Type Size(M) Used(M) Avail(M) Capacity Mounted on\n");
for( i = 0; i < mntsize; i++ )
{
if( strncmp( "/stg", mntbuf[i].f_mntonname, 4) == 0 )
{
int64_t used = mntbuf[i].f_blocks - mntbuf[i].f_bfree;
int64_t availblks = mntbuf[i].f_bavail + used;
printf(" %-12s", mntbuf[i].f_mntfromname );
printf(" %-7s" , mntbuf[i].f_fstypename );
printf(" %10jd" , mntbuf[i].f_blocks * mntbuf[i].f_bsize / 1024 / 1024 );
printf(" %11jd" , (( used * mntbuf[i].f_bsize + 1023) / 1024 + 1023 ) / 1024 );
printf(" %11jd" , mntbuf[i].f_bavail * mntbuf[i].f_bsize / 1024 / 1024 );
printf(" %8.0f%%" , availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
printf(" %s" , mntbuf[i].f_mntonname);
printf("\n");
}
}
return;
}
#include <libgeom.h>
#include <devstat.h>
struct gmesh gmp;
void *sp, *sq;
double dt;
struct timespec tp, tq;
struct devstat *gsp, *gsq;
void print_diskstat()
{
int i;
uint64_t queuelength;
long double busy;
struct gident *gid;
struct gprovider *pp;
struct gconsumer *cp;
sp = geom_stats_snapshot_get();
if (sp == NULL)
err(1, "geom_stats_snapshot()");
geom_stats_snapshot_timestamp(sp, &tp);
dt = tp.tv_sec - tq.tv_sec;
dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9;
tq = tp;
geom_stats_snapshot_reset(sp);
geom_stats_snapshot_reset(sq);
printf("Disk stat:\n");
printf(" Queue_length Busy(%%) Name\n");
for(;;)
{
gsp = geom_stats_snapshot_next(sp);
gsq = geom_stats_snapshot_next(sq);
if (gsp == NULL || gsq == NULL)
break;
if (gsp->id == NULL)
continue;
gid = geom_lookupid(&gmp, gsp->id);
if (gid == NULL)
{
geom_deletetree(&gmp);
i = geom_gettree(&gmp);
if (i != 0)
err(1, "geom_gettree = %d", i);
gid = geom_lookupid(&gmp, gsp->id);
}
if (gid == NULL || gid->lg_what == ISCONSUMER )
continue;
if ( gid->lg_what == ISPROVIDER && ((struct gprovider *)(gid->lg_ptr))->lg_geom->lg_rank != 1)
continue;
if (gsp->sequence0 != gsp->sequence1) {
printf("*\n");
continue;
}
devstat_compute_statistics(gsp, gsq, dt,
DSM_QUEUE_LENGTH, &queuelength,
DSM_BUSY_PCT, &busy,
DSM_NONE);
printf(" ");
printf("%12ju", (uintmax_t)queuelength );
printf(" %6.1lf ", (double)busy );
if (gid == NULL)
{
printf("??");
}
else if (gid->lg_what == ISPROVIDER)
{
pp = gid->lg_ptr;
printf("%s", pp->lg_name);
}
else if (gid->lg_what == ISCONSUMER)
{
cp = gid->lg_ptr;
printf("[CONSUMER] %s/%s/%s",
cp->lg_geom->lg_class->lg_name,
cp->lg_geom->lg_name,
cp->lg_provider->lg_name);
}
printf("\n");
*gsq = *gsp;
}
geom_stats_snapshot_free(sp);
return;
}
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/tcp_var.h>
#include <arpa/inet.h>
#define TCPSTATES
#include <netinet/tcp_fsm.h>
#define C(x) ((u_int)((x) & 0xff))
void print_session()
{
int total = 0 ;
int total_80 = 0;
int total_80_establish = 0;
const char * mibvar = "net.inet.tcp.pcblist";
size_t len = 0;
char * buf = NULL;
printf("TCP Session stat:\n");
if( sysctlbyname( mibvar, 0, &len, 0, 0 ) < 0 )
{
if(errno != ENOENT)
fprintf(stderr, " - sysctlbyname(%s...) get length failed: %s\n", mibvar, strerror(errno));
else
fprintf(stderr, " - sysctlbyname(%s...) not found. OS version check[%s]\n", mibvar, strerror(errno));
return;
}
buf = malloc(len);
if( buf == NULL )
{
fprintf(stderr, " - malloc error :%lu byte [%s]\n", (u_long)len, strerror(errno));
return;
}
if( sysctlbyname( mibvar, buf, &len, 0, 0 ) < 0 )
{
fprintf(stderr, " - sysctlbyname(%s...) get data failed: %s\n", mibvar, strerror(errno));
free(buf);
return;
}
/*
struct xinpgen *xig, *oxig;
struct xsocket *so;
struct xtcp_timer *timer;
struct tcpcb *tp = NULL;
struct inpcb *inp;
const char * vchar;
char line[80];
int port;
oxig = xig = (struct xinpgen *)buf;
printf(" %-5.5s %-6.6s %-6.6s %-22.22s %-22.22s (state)\n", "Proto", "Recv-Q", "Send-Q", "Local Address", "Foreign Address");
for (xig = (struct xinpgen *)((char *)xig + xig->xig_len)
; xig->xig_len > sizeof(struct xinpgen)
; xig = (struct xinpgen *)((char *)xig + xig->xig_len))
{
timer = &((struct xtcpcb *)xig)->xt_timer;
tp = &((struct xtcpcb *)xig)->xt_tp;
inp = &((struct xtcpcb *)xig)->xt_inp;
so = &((struct xtcpcb *)xig)->xt_socket;
// Ignore sockets for protocols other than the desired one.
if (so->xso_protocol != IPPROTO_TCP)
continue;
// Ignore PCBs which were freed during copyout.
if (inp->inp_gencnt > oxig->xig_gen)
continue;
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
vchar = ((inp->inp_vflag & INP_IPV4) != 0) ? "4 " : " ";
if ((tp->t_flags & TF_TOE) != 0)
printf(" %-3.3s%-2.2s ", "toe", vchar);
else
printf(" %-3.3s%-2.2s ", "tcp", vchar);
printf("%6u %6u ", so->so_rcv.sb_cc, so->so_snd.sb_cc);
if (inp->inp_vflag & INP_IPV4)
{
//inetprint(&inp->inp_laddr, (int)inp->inp_lport, "tcp", 1);
inp->inp_laddr.s_addr = ntohl(inp->inp_laddr.s_addr);
port = ntohs((u_short)inp->inp_lport);
if( inp->inp_laddr.s_addr == INADDR_ANY)
{
sprintf( line, "*");
}
else
{
sprintf( line, "%u.%u.%u.%u"
, C(inp->inp_laddr.s_addr >> 24)
, C(inp->inp_laddr.s_addr >> 16)
, C(inp->inp_laddr.s_addr >> 8)
, C(inp->inp_laddr.s_addr));
}
if(port == 0 )
{
strcat( line, ".* ");
}
else
{
char temp[80];
sprintf( temp, ".%d ", port);
strcat( line, temp);
if( port == 80 )
{
total_80++;
if( tp && tp->t_state == 4 )
total_80_establish++;
}
}
printf("%-22.22s ", line);
//inetprint(&inp->inp_faddr, (int)inp->inp_fport, name, 1);
inp->inp_faddr.s_addr = ntohl(inp->inp_faddr.s_addr);
port = ntohs((u_short)inp->inp_fport);
if( inp->inp_faddr.s_addr == INADDR_ANY)
{
sprintf( line, "*");
}
else
{
sprintf(line, "%u.%u.%u.%u"
, C(inp->inp_faddr.s_addr >> 24)
, C(inp->inp_faddr.s_addr >> 16)
, C(inp->inp_faddr.s_addr >> 8)
, C(inp->inp_faddr.s_addr));
}
if(port == 0 )
{
strcat( line, ".* ");
}
else
{
char temp[80];
sprintf( temp, ".%d ", port);
strcat( line, temp);
}
printf("%-22.22s ", line);
}
//printf(" %d", tp->t_state);
if (tp->t_state < 0 || tp->t_state >= TCP_NSTATES)
printf("%d", tp->t_state);
else
printf("%s", tcpstates[tp->t_state]);
printf("\n");
total++;
}
*/
struct xinpgen *xig, *oxig;
struct xsocket *so;
struct inpcb *inp;
struct tcpcb *tp = NULL;
int port;
oxig = xig = (struct xinpgen *)buf;
for (xig = (struct xinpgen *)((char *)xig + xig->xig_len)
; xig->xig_len > sizeof(struct xinpgen)
; xig = (struct xinpgen *)((char *)xig + xig->xig_len))
{
so = &((struct xtcpcb *)xig)->xt_socket;
inp = &((struct xtcpcb *)xig)->xt_inp;
tp = &((struct xtcpcb *)xig)->xt_tp;
// Ignore sockets for protocols other than the desired one.
if( so->xso_protocol != IPPROTO_TCP )
continue;
// Ignore PCBs which were freed during copyout.
if( inp->inp_gencnt > oxig->xig_gen )
continue;
// IP v4, v6 가 아닌 경우 제외
if( (inp->inp_vflag & INP_IPV4) == 0 && (inp->inp_vflag & INP_IPV6) == 0 )
continue;
total++;
// Local port 정보 주출.
port = ntohs((u_short)inp->inp_lport);
if( port == 80 )
{
total_80++;
if( tp && tp->t_state == 4 )
total_80_establish++;
}
}
free(buf);
printf(" TCP session : %5d\n", total );
printf(" TCP 80 port session : %5d\n", total_80 );
printf(" TCP 80 port establish session: %5d\n", total_80_establish );
return;
}
void print_fhsinfo( struct fhs_info * fhs )
{
// Hostname
printf("\nHostname : %s", fhs->hostname );
// Load average
/*
printf("\nLoad averages");
for (i = 0; i < 3; i++)
{
printf("%c %5.2f", i == 0 ? ':' : ',', fhs->load_avg[i] );
}
*/
// 5분 정보만 사용
printf("\nLoad average (5min) : %5.2f", fhs->load_avg[1]);
// CPU
// - 변동률이 커서 사용하지 않는다.
/*
char *cpustatenames[CPUSTATES] = { "user,", "nice,", "system,", "interrupt,", "idle" };
printf("\nCPU: ");
for( i=0; i < CPUSTATES; i++)
{
printf( "%4.1f%% %s", fhs->cpu_stat[i], cpustatenames[i]);
}
*/
// Memory
// - 사용률 정보만 사용 ( total - free )/total * 100
/*
char *memorynames[] = { "M Active, ", "M Inact, ", "M Wired, ", "M Cache, ", "M Buf, ", "M Free" };
printf("\nMem: ");
for( i=0; i < 6; i++ )
{
printf("%d%s", fhs->memory_stat[i], memorynames[i]);
}
*/
printf("\nMemory usage (%%) : %3.0f%%\n - total: %8dM\n - free : %8dM"
, (fhs->memory_total - fhs->memory_stat[5]) * 100.0 / fhs->memory_total
, fhs->memory_total
, fhs->memory_stat[5]
);
// Swap
// - 사용률 정보만 사용
/*
char *swapnames[] = { "M Total, ", "M Used, ", "M Free, ", "% Inuse, ", "K In, ", "K Out" };
printf("\nSwap: ");
for( i=0; i < 6; i++ )
{
printf("%d%s", fhs->swap_stat[i], swapnames[i]);
}
*/
printf("\nSwap usage (%%) : %d%%\n - total: %8dM\n - used : %8dM"
, fhs->swap_stat[3]
, fhs->swap_stat[0]
, fhs->swap_stat[1] );
// Mount info
print_mountinfo();
// Disk stat
print_diskstat();
// Network info
printf("Network stat:\n");
if( fhs->network_now.check_time.tv_sec == 0 && fhs->network_now.check_time.tv_nsec == 0 )
{
printf(" - not vaild");
}
else
{
double elapsed = 0.0;
elapsed = (fhs->network_now.check_time.tv_sec - fhs->network_old.check_time.tv_sec)
+ ((fhs->network_now.check_time.tv_nsec - fhs->network_old.check_time.tv_nsec) * 1e-9);
printf(" check period : %12.1f sec\n", elapsed );
printf(" inbound packets : %12lu\n", fhs->network_now.inbound_packets - fhs->network_old.inbound_packets );
printf(" outbound packets: %12lu\n", fhs->network_now.outbound_packets - fhs->network_old.outbound_packets );
printf(" inbound traffic : %12.0f Byte/sec\n", (fhs->network_now.inbound_bytes - fhs->network_old.inbound_bytes) / elapsed );
printf(" outbound traffic: %12.0f Byte/sec\n", (fhs->network_now.outbound_bytes - fhs->network_old.outbound_bytes) / elapsed );
}
// Session info
print_session();
//printf("\n");
return;
}
/* Log base 2 of 1024 is 10 (2^10 == 1024) */
#define LOG1024 10
int main ()
{
struct fhs_info fhs;
// For Memory
int pagesize = getpagesize();
pageshift = 0;
while (pagesize > 1)
{
pageshift++;
pagesize >>= 1;
}
/* we only need the amount of log(2)1024 for our conversion */
pageshift -= LOG1024;
// For Swap
kd = kvm_open(NULL, "/dev/null", NULL, O_RDONLY, "kvm_open");
if (kd == NULL)
return (-1);
// Disk stat
int i;
int error;
i = geom_gettree(&gmp);
if (i != 0)
err(1, "geom_gettree = %d", i);
error = geom_stats_open();
if (error)
err(1, "geom_stats_open()");
sq = NULL;
sq = geom_stats_snapshot_get();
if (sq == NULL)
err(1 , "geom_stats_snapshot()");
geom_stats_snapshot_timestamp(sq, &tq);
// Print system info
while(1)
//for( int count = 0 ; count < 1000; count++ )
{
get_loadavg ( &fhs );
get_cpuusage( &fhs );
get_memory ( &fhs );
get_swap ( &fhs );
get_hostname( &fhs );
get_network ( &fhs );
print_fhsinfo( &fhs );
//printf("\n%-*s %s", 20, "FileSystem", "aaa");
sleep(1);
}
kvm_close( kd );
return 0;
}