#include "ShmMonitor.h"

#include "ShmDefine.h"
#include "ShmControl.h"

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>


// 프로세스 name 
#define MONITOR_PROG_NAME         "rc_show"


// 화면 갱신 time (sec)
#define DEFAULT_REFRESH_TIME		3


// Shared memory key 파일.
// 2018-02-21 CHG huibong /tmp 경로 대신 /user/service 하위에 생성되도록 경로 수정 (#31701)
// - /tmp 경로 사용시 자동 clean 처리 모듈 동작으로 삭제되는 상황 발생하여 수정 처리.
#define SHARED_MEMORY_KEY_FILE    "/user/service/rc_mond.shm"


// 공유메모리 생성, 삭제 등의 Manager 객체에 대한 포인터.
CShmControl< struct RcInfo > * g_pShmCtl = NULL;


// CHG 2015-11-03 huibong
// - 일감 #24963 에 따른 기능 추가 수행.


// 사용방법 표시
void PrintUsage()
{
	fprintf( stderr, "\n" );
	fprintf( stderr, "Usage: " MONITOR_PROG_NAME " [OPTION] \n" );
	fprintf( stderr, "Options:                                              \n" );

	fprintf( stderr, "  -i {sec}                 Set refresh interval (sec) \n" );	
	fprintf( stderr, "  -h                       Display help information   \n" );
	fprintf( stderr, "  -v                       Display version            \n" );

	fprintf( stderr, "\n" );
	fprintf( stderr, MONITOR_PROG_NAME " is Solbox Cloud Storage module.\n" );
	fprintf( stderr, "    - Display shared memory info that FHS system, service status collected by %s.\n", PROG_NAME );
	fprintf( stderr, "\n" );
	
	return;
}


// 버전 정보 표시
void PrintVersion()
{
	fprintf( stderr, "\n" );
	fprintf( stderr, MONITOR_PROG_NAME " version: " PROG_VERSION "\n\n" );

	return;
}


int main( int argc, char * argv[] )
{
	int iRefreshTime = DEFAULT_REFRESH_TIME;

	if( argc >= 2 )
	{
		int opt;
		while( ( opt = getopt( argc, argv, "hvi:" ) ) != -1 )	// 옵션 끝까지 파싱처리
		{
			switch( opt )
			{
				case 'h':
				case '?':	// 정의되지 않은 문자가 나타날 경우 getopt 에 자동반환. 도움말 표시 처리. 
					PrintUsage();
					return EXIT_SUCCESS;

				case 'v':
					PrintVersion();
					return EXIT_SUCCESS;				

				case 'i':					
					iRefreshTime = atoi( optarg );
					break;				

				default:	// 본 조건절은 getopt 특성상 동작하지 않지만...업무 Flow 이해(?)를 위해 유지한다.
					fprintf( stderr, "Unkonwn options[%c] used. program terminated.\n", opt );
					return EXIT_FAILURE;
			}
		}
	}

	// iRefreshTime 정합성 검사
	// - atoi 에서 오류시 0 을 반환하므로... 무한 refresh 방지.
	if( iRefreshTime <= 0 )
		iRefreshTime = DEFAULT_REFRESH_TIME;
	

	// 공유메모리 접근 처리
	key_t iShmKey;

	// 공유메모리 key 생성을 위한 임시 파일 생성 처리.
	FILE * pShmFile = fopen( SHARED_MEMORY_KEY_FILE, "r" );
	if( pShmFile != NULL )
	{
		// Key file 생성이 성공한 경우... 파일만 있으면 되니까.. close 처리한다.
		fclose( pShmFile );
		pShmFile = NULL;
	}
	else
	{
		// Key file 생성에 실패한 경우...		
		int errorNum = errno;
		fprintf(stderr, "Shared memory key file open fail[%s] [%d][%s]\n\n", SHARED_MEMORY_KEY_FILE, errorNum, strerror( errorNum ) );

		return EXIT_FAILURE;
	}

	// 임시 파일로 부터 key 값 추출
	iShmKey = ftok( SHARED_MEMORY_KEY_FILE, 1 );
	if( iShmKey == (key_t)-1 )
	{
		// 공유메모리 Key 생성 실패시
		fprintf( stderr, "Shared memory key create failed. temp file [%s]\n\n", SHARED_MEMORY_KEY_FILE );		
		return EXIT_FAILURE;
	}

	// 공유메모리에 생성 담당 class 객체를 생성 및 공유메모리 생성 or attach
	CShmControl< struct RcInfo > shmCtl;
	if( shmCtl.Attach( iShmKey, sizeof( struct RcInfo ) ) == false )
	{
		fprintf( stderr, "Shared memory attach failed. key[%d] size[%lu].\n\n", iShmKey, sizeof( struct RcInfo ) );		
		return EXIT_FAILURE;
	}
	g_pShmCtl = &shmCtl; // 전역 포인터에 대한 포인터 지정.	


	// 종료 처리 관련 시그널 처리기 
	signal( SIGINT, ProgramExit );


	struct RcInfo * pShm = shmCtl.GetShmPtr();
	struct ClientInfo * pClient = NULL;

	while( 1 )
	{

		// 중간에 rc_mond 가 종료되었는지 체크...
		pShmFile = fopen( SHARED_MEMORY_KEY_FILE, "r" );
		if( pShmFile == NULL )
		{
			// Key file open check 실패시..			
			fprintf( stderr, "\nshared memory key file not found [%s]. program exit.\n\n", SHARED_MEMORY_KEY_FILE );
			break;
		}
		else
		{
			// key 파일 정상인 경우...			
			fclose( pShmFile );
			pShmFile = NULL;
		}


		system( "clear" );

		printf( "refresh time     : %3d sec\n", iRefreshTime );
		printf( "max client index : %3d\n", pShm->client_max_index );
		printf( "svc - 0:ok,  1:manual out,  2:service disable,  3:read only\n\n");
		//printf( "app - 0:csagentd,  1:vmasd\n\n" );

		//printf( "shm\tvalid\tip\t\tapp\thost\t\t\tupdate\t\tsvc\tupdate\t\tload\t\tmem\tswap\tstg\tdisk\t\tnet\t\ttcp\n" );
		printf( "shm\tip\t\thost\t\t\t\tupdate\t\tsvc\tupdate\t      load\t\tmem\tswap\tstg\tdisk\t\ttraffic\t\t  tcp\n" );
		printf( "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" );

		for( int i = 0; i <= pShm->client_max_index; i++ )
		{
			pClient = &(pShm->client[i]);

			// 2018-02-22 CHG huibong #31703
			// 유효하지 않은 세션이지만.. garbage clean thread 에 의해 처리되기 전까지 존재하는 FHS 항목 표시 안되록 수정.
			// 운영자들이 오해할 가능성 존재해서 정상적인 FHS 만 표시되도록 수정

			if( pClient->use != 0 && pClient->valid  != 0)
			{
				//printf( "%3d\t%d\t%s\t%d\t%s\t%10ld\t%d\t%10ld\t%.2lf %.2lf %.2lf\t%3d\t%3d\t%3d\t%3ju %4.1lf %10ju %10ju\t%5u %5u %5u\n"
				printf( "%3d\t%s\t%-31.30s\t%10ld\t%d\t%10ld    %.2lf %.2lf %.2lf\t%3d\t%3d\t%3d\t%3ju %6.1lf %10ju %10ju  %5u %5u %5u\n"
						, i, pClient->client_ip
						, pClient->hostname
						, pClient->service.update_time, pClient->service.service_stat
						, pClient->system.update_time
						, pClient->system.load_avg[0], pClient->system.load_avg[1], pClient->system.load_avg[2]
						, pClient->system.memory_usage
						, pClient->system.swap_usage
						, pClient->system.storage_max_usage
						, pClient->system.disk_max_queue, pClient->system.disk_max_busy
						, pClient->system.network_traffic_in, pClient->system.network_traffic_out
						, pClient->system.tcp_80_establish, pClient->system.tcp_80, pClient->system.tcp_total
						);
			}
		}
		
		sleep( iRefreshTime );
	}

	
	g_pShmCtl = NULL;
	shmCtl.Detach();

	return 0;
}


void ProgramExit( int iSigno )
{
	if( g_pShmCtl != NULL )
	{
		g_pShmCtl->Detach();
		g_pShmCtl = NULL;
	}

	fprintf( stderr, "\nrc monitoring end by user... goog bye ^^...\n\n" );
	
	signal( SIGINT, SIG_DFL );
	exit( 0 );	
}
