/*******************************************************************************
 * Copyright (c) 2005, 2009 Intel Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Hoang M Nguyen, Intel - Initial API and Implementation
 *
 * $Id: TransportSupportSharedMem.c,v 1.22 2009/07/30 15:25:25 jwest Exp $
 *
 *******************************************************************************/


#include "RAComm.h"
#include "RASharedMemory.h"

#define __TPTPCOMMON_H__

#include "tptp/TransportSupportSharedMem.h"



#include "tptp/NoLog.h"

#ifdef _WIN32
#else
	#include <sys/types.h>
#endif
/* Required for getting system parameters */
#if defined __linux__
	#include <sys/ipc.h>
	#include <sys/shm.h>
	
	#ifndef	IPC_INFO
		#define 	IPC_INFO	3
	#endif
#endif

int myGlobalSock = -1;


/* Shared memory specifics */
#define RA_SHM_BUF_NAME_ROOT  "rabuffer"
#define RA_SHM_BUF_SIZE_DEFAULT   (OSS_RAMBO_CHUNK_SIZE*8) /* shared memory buffer size = 1 Mb */
#define RA_SHM_BUF_SIZE_MIN       OSS_RAMBO_BUFFER_SIZE_MIN  /* shared memory buffer minimum size = 128 kb - 198757 */



/************************************************************************ 
* Validate whether the shared memory size requested is below the system 
* defined max value or not. If the requested size is more than system defined 
* size, change the requested size to a value lower than system defined max 
* value.  
*
* @param size - the requested shared memory segment size
*************************************************************************/

int validateSystemShmMax(int size) {
	int overhead = 8192; // SW: -8KB overhead, need to use a macro if defined
	int shmmax = 0; // max shared memory segment size

#ifdef _AIX
	shmmax = 268435456; // Fixed at 256MB = 268435456 bytes

#elif _SOLARIS
	FILE *fp;
	char *buf;
	char *tail;
	int buf_sz = 80; // line width

	buf = (char*)tptp_malloc(buf_sz * sizeof(char));
	fp = popen("/usr/sbin/sysdef | grep SHMMAX | awk \'{ print $1 }\'", "r");
	fgets(buf, buf_sz, fp);
	shmmax = (unsigned long)strtoul(buf, &tail, 0);
	if(shmmax == 0) { // error parsing the value
		TPTP_LOG_ERROR_MSG("Cannot get system shared memory setting");
		shmmax = 1048576; // default on Solaris
	}
	fclose(fp);
	tptp_free(buf);

#elif _HPUX
	FILE *fp;
	char *buf;
	char *tail;
	int buf_sz = 80; // line width

	buf = (char*)tptp_malloc(buf_sz * sizeof(char));
	fp = popen("kmtune -q shmmax | grep shmmax | awk '{ print $2 }'", "r");
	fgets(buf, buf_sz, fp);
	shmmax = (unsigned long)strtoul(buf, &tail, 0);
	if(shmmax == 0) { // error parsing the value
		TPTP_LOG_ERROR_MSG("Cannot get system shared memory setting");
		shmmax = 67108864; // default on HP-UX
	}
	fclose(fp);
	tptp_free(buf);

#elif __linux__
	int rc;
	struct shminfo info;
	
	rc = shmctl(0, IPC_INFO, (struct shmid_ds *) (void*) &info); // to make compiler happy
	if (rc >= 0)
		shmmax = info.shmmax;
	 
	if(shmmax <= 0) {
		TPTP_LOG_ERROR_MSG("Cannot get system shared memory setting");
		shmmax = 33554432; // default for Linux systems
	}
#elif __OS400__
	shmmax = 16773120; /* 236851 */

#else // _WIN32 || MVS
	return size; // Anyone knows how to check the max shm size?

#endif

	if(overhead > shmmax) {
		TPTP_LOG_ERROR_MSG1("System shared memory segment size too small: %lu", shmmax);
		return 0;
	}
	else if(size > (shmmax - overhead)) {
		TPTP_LOG_DEBUG_MSG3("Requesting shared memory %lu bytes, allocating %lu bytes, system max %lu bytes", size, shmmax - overhead, shmmax);
		return (shmmax - overhead);
	}
	else {
		TPTP_LOG_DEBUG_MSG3("Requesting shared memory %lu bytes, allocating %lu bytes, system max %lu bytes", size, size, shmmax);
		return size;
	}

}


/**
 *********************************************************
 *
 * @brief
 *    Write given data to the given IPC memory resource.
 *
 * @param  pBlockInfo
 *              the info of the shared memory map to write to
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/

int ipcMemWrite(
      char * pFromBuffer,
      int bufferLength,
	  mem_map_block_ptr_t pBlockInfo)
{
	int    rc = 0 ;

	ra_shm_handle_t *pShmHdl = (ra_shm_handle_t *) pBlockInfo->pRamboBlock ;
	if (pShmHdl == NULL) return -1;

	rc = ra_writeToShm(pShmHdl, pFromBuffer, bufferLength) ;

	return ( rc ) ;
}



/**
 *********************************************************
 *
 * @brief
 *    Write given data to the given IPC memory resource.
 *
 * @param  pBlockInfo
 *              the info of the shared memory map to write to
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/

int ipcMemWriteWithDIME(
      char * pFromBuffer,
      int bufferLength,
      char * dimeHeader,
      int dimeLength,
	  mem_map_block_ptr_t pBlockInfo)
{
	int    rc = 0 ;
	char *new_buffer = tptp_malloc(dimeLength + bufferLength);

	ra_shm_handle_t *pShmHdl = (ra_shm_handle_t *) pBlockInfo->pRamboBlock ;

	/* these 2 copies are bad... need to fix this!! */
	memcpy(new_buffer, dimeHeader, dimeLength);
	memcpy((new_buffer + dimeLength), pFromBuffer, bufferLength);

	rc = ra_writeToShm(pShmHdl, new_buffer, dimeLength + bufferLength);
	tptp_free(new_buffer);

	return ( rc ) ;
}

/**
 *********************************************************
 *
 * @brief
 *    Write given data to the given IPC memory resource.
 *
 * @param  pBlockInfo
 *              the info of the shared memory map to write to
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/

int ipcMemWriteRaw(
      char * pFromBuffer,
      int bufferLength,
	  mem_map_block_ptr_t pBlockInfo)
{
	int    rc = 0 ;

	ra_shm_handle_t *pShmHdl = (ra_shm_handle_t *) pBlockInfo->pRamboBlock ;

	rc = ra_writeToShm(pShmHdl, pFromBuffer, bufferLength) ;

	return ( rc ) ;
}



/**
 *********************************************************
 *
 * @brief
 *    Destroy the given IPC memory resource.
 *
 * @param  pBlockInfo
 *              the info of the shared memory map to clean up
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/

int ipcMemDestroy(mem_map_block_ptr_t pBlockInfo)
{
	ra_shm_handle_t *pShmHdl = (ra_shm_handle_t *) pBlockInfo->pRamboBlock ;
	if (pShmHdl == NULL) return -1;
	
	ra_destroyShm( pShmHdl );

	pBlockInfo->pRamboBlock = NULL ;

	return ( 0 ) ;
}



/**
 *********************************************************
 *
 * @brief
 *    Set up data processing and monitor data for the given shared memory block
 *
 * @param  pBlockInfo
 *              the info of the shared memory map to monitor
 *
 * @param  pDataProcessor
 *              the function to be called whenever data are available
 *
 * @param  pArgs
 *              any paramater need to be passed along with the data to the function
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *
 * @note
 *    This is going to loop waiting for data to read and pass to the given function
 *    until it is stopped explicitly by calling ipcStopFlushing() function.
 *
 *********************************************************/
int  ipcFlushToFunc(mem_map_block_ptr_t pBlockInfo,
      TPTPDataProcessingFunc pDataProcessor,
	  void *pArgs )
{
	int rc = 0 ;

	ra_shm_handle_t *pShmHdl = (ra_shm_handle_t *) pBlockInfo->pRamboBlock ;

	rc = ra_flushShmToFunc(pShmHdl, pDataProcessor, pArgs) ;

	return ( rc ) ;
}

int  ipcFlushToFD(mem_map_block_ptr_t pBlockInfo,
      int fd,
      int desc)
{
	int rc = 0 ;

	ra_shm_handle_t *pShmHdl = (ra_shm_handle_t *) pBlockInfo->pRamboBlock ;

	rc = ra_flushShmToFD(pShmHdl, fd, desc) ;

	return ( rc ) ;
}

/**
 *********************************************************
 *
 * @brief
 *    Create an IPC memory resource.
 *
 * @param  pMemName
 *              the name of shared memory resource
 *
 * @param  pBlockInfo
 *              the info of the returned shared memory map
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
int ipcMemCreate(
      const char * pMemName,
	  mem_map_block_ptr_t pBlockInfo,
	  tptp_uint32 requestedSize ) 
{
	int  rc = 0 ;

	ra_shm_err_t shmerr ;

	ra_shm_handle_t *pShmHdl = NULL ;

	ra_uint_t shmBufSize;
	
	if ( requestedSize == 0 )
	{
		shmBufSize = RA_SHM_BUF_SIZE_DEFAULT;   /* default size - 198757 */
	}
	else
	{
		shmBufSize = requestedSize;
	}

	TPTP_LOG_DEBUG_MSG("ipcMemCreate...") ;

	// printf("sizeof ra_shm_handle_t %d\n", 	sizeof(ra_shm_handle_t)); // jgw-shmem
	pShmHdl = (ra_shm_handle_t *) tptp_malloc(sizeof(ra_shm_handle_t)) ;
	if (pShmHdl == NULL) rc = -2 ;

	if (rc == 0)
	{
		if (shmBufSize < RA_SHM_BUF_SIZE_MIN)
			shmBufSize = RA_SHM_BUF_SIZE_MIN ;

		shmBufSize =  validateSystemShmMax(shmBufSize);

		TPTP_LOG_DEBUG_MSG2("Shared mem name(%s) size(%d)", pMemName, shmBufSize) ;
		shmerr = ra_createShm((char*) pMemName, shmBufSize, pShmHdl) ;

		if (shmerr != 0)
		{
			TPTP_LOG_ERROR_MSG1("Shared mem error(%d)", shmerr) ;
			rc = -3 ;
		}
	}

	if (rc == 0)
	{
		disableAllSecurityOfKernalObject(ra_getShmRealHdl(pShmHdl)) ;
	}

	if (rc == 0)
		pBlockInfo->pRamboBlock = (LPVOID) pShmHdl ;
	else
		pBlockInfo->pRamboBlock = NULL ;

	return ( rc ) ;
}
