#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;
}

