/***************************************************************************
		rc_mond shared memory template header
	-----------------------------------------
	begin      : 2015/06/15
	copyright  : (C) 2005 Solbox Inc.
	author     : Storage Dev Team
	email      : huibong@solbox.com
	version    : 3.5

CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
***************************************************************************/

// 본 코드는 시스템 공유메모리 생성, 접근, 해제, 삭제 처리를 수행하기 위한 코드로서
// template 코드 방식으로 구성

#ifndef __SHM_CONTROL_H__
#define __SHM_CONTROL_H__


#include <unistd.h>
#include <sys/shm.h>
#include <string.h>


/*----------- MODE Flag ---------------
   SHM_R        0400  read  by user
   SHM_W        0200  write by user
   SHM_R >> 3   0040  read  by group
   SHM_W >> 3   0020  write by group
   SHM_R >> 6   0004  read  by others
   SHM_W >> 6   0002  write by others
-------------------------------------*/

////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief ShmControl 공유메모리 관련 Template Class
template <typename T>
class CShmControl
{
	public:
		/// @brief 생성자
		CShmControl();
		/// @brief 소멸자
		~CShmControl();
	
	
		/// @brief 공유메모리 생성, 접근 또는 기존 공유메모리 접근.
		/// @param iShmKey 공유메모리 접근을 위한 키
		/// @param iSize 접근하고자 하는 공유메모리의 크기
		/// @param iSize 공유메모리 생성및 접근시 권한
		/// @return return true or false
		bool Create( key_t iShmKey, size_t iSize, int iModeFlag = SHM_R | SHM_W );
		
		/// @brief 기존 공유메모리 접근.
		/// @param iShmKey 공유메모리 접근을 위한 키
		/// @param iSize 접근하고자 하는 공유메모리의 크기
		/// @param iSize 공유메모리 접근시 권한
		/// @return return true or false
		bool Attach( key_t iShmKey, size_t iSize, int iModeFlag = SHM_R | SHM_W );
		
		/// @brief 기존 접근한 공유메모리와의 연결을 해제
		/// @return return true or false		
		bool Detach( void );
		
		/// @brief 기존 접근한 공유메모리와의 연결을 해제 및 삭제
		/// @return return true or false
		bool Remove( void );

		/// @brief 공유메모리에 대한 포인터를 반환
		/// @return 생성 및 접근한 공유메모리에 대한 포인터 또는 오류 발생시 NULL
		T * GetShmPtr(void) const { return m_pShm; }
		
		/// @brief 접근한 공유메모리의 ID 값을 반환
		/// @return 생성 및 접근한 공유메모리 ID Value에 대한 참조.
		int GetShmId(void) const  { return m_iShmId; }

	private:
		///< m_pShm 공유메모리에 대한 포인터
		T * m_pShm;
		///< m_pShmId 접근한 공유메모리의 ID Value
		int m_iShmId;
};

////////////////////////////////////////////////////////////////////////////////////////////////

// 기본 멤버변수 초기화.
template < typename T >
inline CShmControl<T>::CShmControl()
	: m_pShm(NULL)
	, m_iShmId(-1)
{	
}

template < typename T >
inline CShmControl<T>::~CShmControl()
{
	// 생성된 공유메모리를 종료시 다른 프로세스들이 사용한다면.. 맘대로 삭제해서는 안되며...
	// 이럴 경우 detach 처리만 해야 한다.
	// Detach();

	// 이와 반대로.. 다른 프로세스에서 더이상 해당 공유메모리를 사용하지 않을 경우.. remove 처리한다.
	Remove();

}


// 해당 공유메모리를 생성한다.  만약 기존에 생성된 것이 있다면  그놈을 사용하도록 한다.
template < typename T >
inline bool CShmControl<T>::Create( key_t iShmKey, size_t iSize, int iModeFlag )
{	
	// 우선 공유 메모리를 생성해 봅니다.
	m_iShmId = shmget( iShmKey, iSize, IPC_CREAT | IPC_EXCL | iModeFlag );
	// 생성에 실패한 경우.
	if( m_iShmId == -1)   
	{
		// 이미 기존에 생성된 놈이 있는지 확인해 봅시다.		
		return Attach( iShmKey, iSize, iModeFlag);
	}
	// 생성에 성공한 경우.
	else
	{	
		// 생성된 공유 메모리의 포인터를 받아옵니다.
		m_pShm = (T *)shmat( m_iShmId, 0, 0 );
		
		// 받아온 공유 메모리 포인터 값이 정확하면
		if( reinterpret_cast<long>(m_pShm) != -1)
		{
			// 원래 시스템에서 생성시 자동으로 초기화됨  => 그렇지만 새로이 생성을 하였으므로 확인 사살 초기화 ^^
			memset( m_pShm, 0x00, iSize);
			return true;
		}
		// 받아온 공유 메모리 포인터를 가져오는데 실패한 경우
		// => 만든놈이 삭제해 버립시다.
		else
		{
			Remove();
			return false;
		}
	}
}

// 기존에 생성된 것 공유 메모리에 접근하기 위해서 사용한다..
// 키값과 크기값을 정확히 알고 있어야 ( 혹시 다른데서 사용하는 놈으로 붙을까봐 ) 접근할 수 있도록 한다.
template < typename T >
inline bool CShmControl<T>::Attach( key_t iShmKey, size_t iSize, int iModeFlag )
{
	// 기존 연결은 해제 시켜 버린다. => 그냥 놓은 상태에서 다시 접속할 경우 메모리 사용량이 올라감.
	Detach();
		
	m_iShmId = shmget( iShmKey, iSize, iModeFlag);
	if( m_iShmId == -1 )
		return false;
	else
	{
		// 우선 기존에 있는 것과 크기가 동일한지 검사한다.
	    struct shmid_ds shmInfo;
		memset(&shmInfo, 0x00, sizeof(struct shmid_ds));

		shmctl( m_iShmId, IPC_STAT, &shmInfo);
		
		// shm_segsz 정의 관련 참고사항
		// - FreeBSD 6.X  : int type으로 정의되어.. 컴파일시 waring 발생
		// - FreeBSD 10.X : size_t type
		// - CentOS 6.4   : size_t type
		if( shmInfo.shm_segsz != iSize)
			return false;
		else
		{
	
			m_pShm = (T *)shmat( m_iShmId, 0, 0 );

			// reinterpret_cast 을 사용한 것은 운영체제마다 포인터 형이 다를수가 있다고 참고한 라이브러리에 나와있어서 함.
			if( reinterpret_cast<long>(m_pShm) == -1)
				return false;
			else
				return true;
		}				
	}
}

// 접근한 공유메모리로 부터 분리시킨다. ( 접근시 설정된 멤버 변수 이용)
template < typename T >
inline bool CShmControl<T>::Detach()
{
	if( m_pShm > 0 )
	{
		if( shmdt( m_pShm)  == 0)
		{
			m_iShmId = -1;
			m_pShm = NULL;
			return true;
		}
		else
			return false;
	}
	else
		return true;
}	

// 공유메모리를 삭제한다.  이 작업을 하기 위해서는 먼저 공유 메모리를 생성 또는 접근을 해야하며
// 이때 설정된 멤버 변수를 가지고 삭제 작업을 수행한다.
// 공유 메모리는 여러 Process 들이 사용가능하기 때문에 삭제시 신중을 기할 것.
template < typename T >
inline bool CShmControl<T>::Remove()
{
	if( m_iShmId > -1)
	{
		if( shmctl( m_iShmId, IPC_RMID, NULL) == -1)
			return false;
		else
		{
			m_iShmId = -1;
			m_pShm = NULL;
			return true;
		}
	}
	return true;
}


#endif /* __SHM_CONTROL_H__ */
