// ExclusionMutua.cpp: implementation of the CExclusionMutua class.
//
//////////////////////////////////////////////////////////////////////

//#include "stdafx.h"
#include "ExclusionMutua.h"

#if !defined DBG_MUTEX
	#undef DBG_TRACE
	#undef DBG_DUMP
	#undef DBG_DUMP_TEMPLATE

	#define DBG_TRACE(PARAMS)
	#define DBG_DUMP(PARAMS)
	#define DBG_DUMP_TEMPLATE(PARAMS)
#endif

#define MAX_TIMEOUT_LOCK_MUTEX 10000

#if !defined WIN32
    #include <pthread.h>
    #include <errno.h>
    #include <unistd.h>

//    #define getpid  getpid
    #define gettid  pthread_self
#else
    #define getpid  GetCurrentProcessId
    #define gettid  GetCurrentThreadId

#endif

extern CExclusionMutua g_ExclMutua;

CK_RV NativeCreateMutex(CK_VOID_PTR_PTR ppMutex)
{
	DBG_TRACE(("NativeCreateMutex(0x%08x)...\n",ppMutex));

    CK_RV rvResult = CKR_OK;

    #if defined WIN32
        HANDLE *phHandle = NULL_PTR;
        DWORD err_code;

        PSECURITY_DESCRIPTOR pSD;
        SECURITY_ATTRIBUTES sa;

        pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
              SECURITY_DESCRIPTOR_MIN_LENGTH);

        if (pSD == NULL)
            return CKR_HOST_MEMORY;


        if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
           return CKR_HOST_MEMORY;

        // Add a null DACL to the security descriptor.
        if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE))
            return CKR_HOST_MEMORY;

        sa.nLength = sizeof(sa);
        sa.lpSecurityDescriptor = pSD;
        sa.bInheritHandle = TRUE;

        phHandle = new HANDLE;

        /***********************************************************/
        // Comprobaremos si hemos obtenido ya el nombre del lector,
        // en cuyo caso crearemos el mutex con la etiqueta adecuada.
        if( g_ExclMutua.m_pNombre_Lector.size() != 0 )
        {
            byteBuffer bbNombreCompleto( (byte *)"MutexFNMTRCM", 12 );
            bbNombreCompleto.append( g_ExclMutua.m_pNombre_Lector );

            *phHandle = CreateMutex(&sa, FALSE, (LPCSTR)&bbNombreCompleto[0]);
        }
        else
        {
            // Si aún no lo tenemos crearemos un mutex genérico,
            // cuyo nombre será "MutexCryptoCardFNMT01"
            *phHandle = CreateMutex(&sa, FALSE, "MutexCryptoCardFNMT01");
        }

        if (*phHandle == 0)
        {
            err_code = GetLastError();
            if((err_code == ERROR_NOT_ENOUGH_MEMORY) || ( err_code == ERROR_OUTOFMEMORY ))
                rvResult = CKR_HOST_MEMORY;
            else
                rvResult = CKR_GENERAL_ERROR;
        }

        // Devolvemos el handle del objeto mutex que acabamos de crear
        *ppMutex = phHandle;
    #else
        pthread_mutex_t *pMutex = new(pthread_mutex_t);

        memset( pMutex, 0, sizeof(pthread_mutex_t) );

        //*pMutex = PTHREAD_MUTEX_INITIALIZER;

        if( pthread_mutex_init(pMutex, NULL) != 0 )
        {
            delete pMutex;
            rvResult = CKR_GENERAL_ERROR;
        }

        *ppMutex = pMutex;
    #endif

	DBG_TRACE(("NativeCreateMutex() = 0x%08x  pMutex = 0x%08x...\n",rvResult,*ppMutex));

	return rvResult;
}


CK_RV NativeDestroyMutex(CK_VOID_PTR pMutex)
{
	DBG_TRACE(("NativeDestroyMutex(0x%08x).\n",pMutex));

    #if defined WIN32
        HANDLE hHandle;

        hHandle = *((HANDLE *)pMutex);

        CloseHandle(hHandle);

        delete pMutex;

        pMutex = NULL;
    #else
         pthread_mutex_destroy( (pthread_mutex_t *)pMutex );
         delete (pthread_mutex_t *)pMutex;
    #endif

	return CKR_OK;
}


CK_RV NativeLockMutex(CK_VOID_PTR pMutex)
{
	DBG_TRACE(("NativeLockMutex(0x%08x)...\n",pMutex));

    #if defined WIN32
        HANDLE hHandle;

        hHandle = *((HANDLE *)pMutex);
        if(WaitForSingleObject(hHandle, MAX_TIMEOUT_LOCK_MUTEX) == WAIT_TIMEOUT)
        {
            DBG_TRACE(("NativeLockMutex(0x%08x) TIMEOUT.\n",pMutex));
            return CKR_GENERAL_ERROR;
        }
    #else
        time_t  start = time(NULL);
        int     rc;

        do
        {
            rc = pthread_mutex_trylock((pthread_mutex_t *)pMutex);
            if( rc != EBUSY )
                break;
            sleep(1);
        }
        while( time(NULL)-start < 10 );

        if( rc )
        {
            DBG_TRACE(("NativeLockMutex(0x%08x) TIMEOUT.\n",pMutex));
            return CKR_GENERAL_ERROR;
        }
    #endif

	DBG_TRACE(("NativeLockMutex(0x%08x) = 0\n",pMutex));
	return CKR_OK;
}


CK_RV NativeUnlockMutex(CK_VOID_PTR pMutex)
{
	DBG_TRACE(("NativeUnlockMutex(0x%08x)...\n",pMutex));

    #if defined WIN32
        HANDLE hHandle;
        hHandle = *((HANDLE *)pMutex);

        ReleaseMutex(hHandle);
    #else
        pthread_mutex_unlock((pthread_mutex_t *)pMutex);
    #endif

	DBG_TRACE(("NativeUnlockMutex(0x%08x) = 0\n",pMutex));
	return CKR_OK;
}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CExclusionMutua::CExclusionMutua()
{
	DBG_TRACE(("CExclusionMutua::CExclusionMutua(). [%d.0x%X]\n",getpid(),gettid()));


	m_pFuncCreateMutex	= NULL;
	m_pFuncDestroyMutex = NULL;
	m_pFuncLockMutex	= NULL;
	m_pFuncUnlockMutex	= NULL;
	m_pMutex			= NULL;

	//m_pNombre_Lector	= NULL;
	m_pMutex_Lector		= NULL;
	m_bMutexReady		= false;
	m_ulNumSolic		= 0;

	// Se establecen las funciones Nativas
	SetFunctionsPointer();

	this->CreateMutex();

	/*if (this->CreateMutex() != CKR_OK)
		return FALSE;*/
}


CExclusionMutua::~CExclusionMutua()
{
	DBG_TRACE(("CExclusionMutua::~CExclusionMutua(). [%d.0x%X]\n",getpid(),gettid()));

	if (m_pFuncDestroyMutex != NULL)
	{
		if(m_pMutex != NULL)
		{
			// Destruimos el Mutex original del PKCS11
			m_pFuncDestroyMutex(m_pMutex);

			m_pMutex = NULL;
		}

		// Si existe el Mutex del lector lo destruiremos.
		if(m_pMutex_Lector != NULL)
		{
			// Destruimos el Mutex específico del lector
			m_pFuncDestroyMutex(m_pMutex_Lector);

			m_pMutex_Lector = NULL;
		}
	}
}


CK_RV CExclusionMutua::CreateMutex()
{
	DBG_TRACE(("CExclusionMutua::CreateMutex(). [%d.0x%X]\n",getpid(),gettid()));

	if (m_pFuncCreateMutex != NULL)
	{
		// Si aún no existe ningún mutex (ni el inicial
		// ni el del lector), crearemos el inicial.
		if (m_pMutex == NULL)
		{
			return m_pFuncCreateMutex(&m_pMutex);
		}
		else
		{
			// Si ya existía el inicial crearemos el mutex del lector
			return m_pFuncCreateMutex(&m_pMutex_Lector);
		}
	}

	return CKR_HOST_MEMORY;
}


CK_RV CExclusionMutua::DestroyMutex(BOOL bMutexLector)
{
	DBG_TRACE(("CExclusionMutua::DestroyMutex(%d)... [%d.0x%X]\n",bMutexLector,getpid(),gettid()));
	CK_RV rv = CKR_OK;

	if (m_pFuncDestroyMutex != NULL)
	{
		// Si hemos indicado este parámetro a TRUE
		// destruiremos sólo el mutex del lector, dejando el original.
		if(!bMutexLector)
		{
			if(m_pMutex != NULL)
			{
				// Destruimos el Mutex original del PKCS11
				rv = m_pFuncDestroyMutex(m_pMutex);

				m_pMutex = NULL;

				// devolvemos el resultado, ya que sólo se destruirá un mutex
				// en cada llamada: el original o el del lector, pero nunca ambos.
				if(rv!= CKR_OK)
					return rv;
			}
		}

		// Si existe el Mutex del lector lo destruiremos.
		if(m_pMutex_Lector != NULL)
		{
			// Destruimos el Mutex específico del lector
			rv = m_pFuncDestroyMutex(m_pMutex_Lector);

			m_pMutex_Lector = NULL;
		}

		m_bMutexReady = false;
	}
	else
		rv = CKR_HOST_MEMORY;

	DBG_TRACE(("CExclusionMutua::DestroyMutex()=0x%08x... [%d.0x%X]\n",rv,getpid(),gettid()));
	return rv;
}


CK_RV CExclusionMutua::LockMutex()
{
	DBG_TRACE(("CExclusionMutua::LockMutex()... [%d.0x%X]\n",getpid(),gettid()));

	if (m_pFuncLockMutex != NULL)
	{
		// Si ya tenemos un mutex preparado para el lector,
		// lo utilizaremos.
		if ( (m_bMutexReady) && (m_ulNumSolic==0))
		{
			return m_pFuncLockMutex(m_pMutex_Lector);
		}
		else
		{
			m_ulNumSolic++;

			return m_pFuncLockMutex(m_pMutex);
		}
	}

	return CKR_HOST_MEMORY;
}


CK_RV CExclusionMutua::UnlockMutex()
{
	DBG_TRACE(("CExclusionMutua::UnlockMutex()... [%d.0x%X]\n",getpid(),gettid()));

	CK_RV rvReturn = CKR_OK;

	if (m_pFuncUnlockMutex != NULL)
	{
		// Si teníamos capturado el mutex original, lo liberamos
		if (m_ulNumSolic>0)
		{
			// Ya tenemos el nombre del lector, así que deberemos
			// crear el nuevo Mutex.
			if ( (m_pNombre_Lector.size() != 0) &&
				 (!m_bMutexReady) )
			{
				rvReturn = m_pFuncCreateMutex(&m_pMutex_Lector);

				m_bMutexReady = true;
			}

			m_ulNumSolic--;

			// En cualquier caso deberemos liberar el mutex genérico
			rvReturn = m_pFuncUnlockMutex(m_pMutex);
			return rvReturn;

		}
		else
		{
			// Si ya existe el mutex del lector se libera.
			if (m_bMutexReady)
				return m_pFuncUnlockMutex(m_pMutex_Lector);
		}
	}

	return CKR_HOST_MEMORY;
}


void CExclusionMutua::SetFunctionsPointer(CK_CREATEMUTEX	pFuncCreateMutex,
										  CK_DESTROYMUTEX	pFuncDestroyMutex,
										  CK_LOCKMUTEX		pFuncLockMutex,
										  CK_UNLOCKMUTEX	pFuncUnlockMutex)
{
	if ((pFuncCreateMutex		!= NULL) &&
		(m_pFuncDestroyMutex	!= NULL) &&
		(m_pFuncLockMutex		!= NULL) &&
		(m_pFuncUnlockMutex		!= NULL))
	{
		m_pFuncCreateMutex	= pFuncCreateMutex;
		m_pFuncDestroyMutex = pFuncDestroyMutex;
		m_pFuncLockMutex	= pFuncLockMutex;
		m_pFuncUnlockMutex	= pFuncUnlockMutex;
	}
	else
	{
		m_pFuncCreateMutex	= &NativeCreateMutex;
		m_pFuncDestroyMutex = &NativeDestroyMutex;
		m_pFuncLockMutex	= &NativeLockMutex;
		m_pFuncUnlockMutex	= &NativeUnlockMutex;
	}
}

