/*******************************************************************************
 * 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: SharedMemListener.c,v 1.37 2009/04/14 20:33:27 jwest Exp $
 *
 *******************************************************************************/ 


/**
 *********************************************************

Each transport layer will provide and implement exactly the same 
pre-defined interface. Each will be in its own DLL or shared library
that is dynamically loaded at runtime.

The interaction between Agent Controller (AC) and the transport layer (TL)
is defined in "MsgPipeline.h" header file.

- from AC to TL, functions include "createTransportListener(), 
  destroyTransportListener(), etc.

- from TL to AC, see function callback in "transport_layer_data_t" structure.

  ------
  |    |
  | AC |   
  |    |<-------|
  ------        |
                V
              ------
              |    |
              | TL |
              |    |
              ------
               ^  ^
         |-----|  |---------------|
         |                        |
         V                        V
    ----------                ---------
    |        |                |       |
    | Client |                | Agent |
    |        |                |       |
    ----------                ---------


The interaction between the transport layer (TL) and the client
or the agent is defined in "TPTPMessageHeader.h" (CONNECT, DISCONNECT, etc.)
and basically any command or data.

The TL will process only CONNECT and DISCONNECT commands and forward
everything else to the AC for routing to the right destination for processing.

 *********************************************************/


#include "SharedMemListener.h"
#include "tptp/TPTPSupportUtils.h"
#include "tptp/TPTPUtils.h"
#include "SharedMemTLLog.h"



/** thread status */
enum ThreadStatus { IDLE, RUNNING } ;

static int acShmMemCount = 0;
static int shMemInitialized = 0;

void destroyMem (mem_map_block_ptr_t  pMemBlockInfo);

extern void ossSleep (long ms);

/*====================================================================*/
/*    internal function prototypes                                    */
/*====================================================================*/
THREAD_USER_FUNC_RET_TYPE doListening(LPVOID args) ;
int serveRequest(server_block_ptr_t pServerData) ;
char * processTheMessageHeader(server_block_ptr_t pServerData, char *pMsg, int *pPayLoadLength) ;
int  processCONNECT_DATACall(server_block_ptr_t pServerData, char *pMsg, int payLoadLength) ;
request_block_ptr_t  getInitRequestDataBlock(server_block_ptr_t pServerData) ;
int   sendThisMessage(server_block_t* pServerData, tptp_uint32 connectionID, tptp_uint32 cmdSize, char * pCmdBlock, BOOL shouldAddHeader) ;
THREAD_USER_FUNC_RET_TYPE processDataRequest(LPVOID args) ;


/**
 *********************************************************
 *
 * @brief
 *    create a new instance of a shared mem handler
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/

tptp_int32 createSharedMemListener(tptp_object* cmo, transport_layer_data_t * pTransportData, tptp_object* tlo) 
{
	tptp_int32      rc = 0 ;
	server_block_t* pServerData;
	char*          memName;

	/* We can't use the logging macros here because we haven't set up our server data structure yet */
	if ( pTransportData->logEventEntry )
	{
		pTransportData->logEventEntry( cmo, "Shared Memory TL", pTransportData->transportID, __FILE__, __LINE__, TPTP_DEBUG, "createTransportListener (shared memory)." );
	}


	/* prepare the globally available server data block */
	pServerData = (server_block_ptr_t) malloc(sizeof(server_block_t)) ;
	pServerData->cmo = cmo;
	pServerData->threadStatus = 0 ;
	pServerData->agentControllerDataBlk = *pTransportData ;

	rc = getSharedMemConfigInfo(pTransportData->configurationData, &memName);
	if (rc != -1) 
	{
		/* The memory for this was allocated by the call above */
		pServerData->sharedMemName = memName;
	} 
	else 
	{
		pServerData->sharedMemName = (char*)tptp_malloc(strlen(TPTP_SHM_BUF_NAME_ROOT)+1) ;
		strcpy(pServerData->sharedMemName, TPTP_SHM_BUF_NAME_ROOT);
	}

	/* allocate connection table */
	pServerData->connectionTable = tableCreate();

	tlo->data = pServerData;
	tlo->objectID = SHARED_MEM_LISTENER_OBJECT_ID;

	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    destroy an instance of a shared mem handler
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/

tptp_int32 destroySharedMemListener(tptp_object* tlo)
{
	server_block_t* pServerData = tlo->data;

	/* Clear the object data */
	tlo->data = 0;
	tlo->objectID = -1;

	TPTP_LOG_DEBUG_MSG(pServerData, "destroyTransportListener (named pipe).") ;

	/* stop the running thread */
	pServerData->threadStatus = IDLE ;

	/* TODO: ?? cleanSharedMemUp() ; ?? */

	/* TODO: Make sure that our threads have finished? */

	/* free the connection table */
    tableDelete( pServerData->connectionTable );
	pServerData->connectionTable = NULL ;

	/* free the shared mem name */
	if (pServerData->sharedMemName) free(pServerData->sharedMemName);

	free( pServerData );

	return 0;
}


/**
 *********************************************************
 *
 * @brief
 *    set the function for forwarding messages
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/

tptp_int32 setSharedMemProcessMessageFunc(server_block_t* pServerData, tptp_object* nexto, processMessage_ptr_t func) 
{
	pServerData->processMessage = func;
	pServerData->nexto = nexto;

	return 0;
}

/**
 *********************************************************
 *
 * @brief
 *    create a new running thread to handle shared mem connections
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
tptp_int32 startSharedMemListener(server_block_t* pServerData) 
{
	int              rc = 0 ;
	TID              threadId;
	HANDLE           threadHandle ;
	mem_map_block_t  *pMemBlockInfo ;

	pMemBlockInfo = &(pServerData->serverMemInfo);

	/* Create the shared memory resource */
	rc = ipcMemCreate(pServerData->sharedMemName, pMemBlockInfo, 0);

	if (pMemBlockInfo->pRamboBlock != NULL) {	// free unused memory
		tptp_free(pMemBlockInfo->pRamboBlock);
		pMemBlockInfo->pRamboBlock = NULL;
	}
	
	if ( rc != 0 )
	{
		TPTP_LOG_ERROR_MSG1( pServerData, "Unable to create shared memory: %s.", pServerData->sharedMemName );
	}
	else
	{
		shMemInitialized = 1;
		
		/* create new thread to listen to connection request */
		rc = tptpStartThread(doListening, 
				(LPVOID) pServerData, &threadId, &threadHandle) ;
		CLOSE_THREAD_HANDLE(threadHandle);
	}

	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    stop the shared memory server. Stop accepting connection requests.
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/

tptp_int32   stopSharedMemListener(server_block_t* pServerData)
{
	int cnt;
	
	pServerData->threadStatus = IDLE ;
	ipcCloseMem (&pServerData->serverMemInfo);
	ipcStopFlusher (&pServerData->serverMemInfo);

	cnt = 20;
	while (shMemInitialized && cnt-- > 0) {	// wait 2 sec for flusher termination
		ossSleep (100L);	
	} 
	
	destroyMem (&pServerData->serverMemInfo);
	
	return 0 ;
}

/**
 *********************************************************
 *
 * @brief
 *    destroy shared memory block
 *
 *********************************************************/

void destroyMem (mem_map_block_ptr_t  pMemBlockInfo) {
	if (shMemInitialized) {
		shMemInitialized = 0;
		ipcStopFlushing (pMemBlockInfo); // let shared memory get deleted when it is destroyed
		ipcMemDestroy (pMemBlockInfo);
	}
}


/**
 *********************************************************
 *
 * @brief
 *    terminate the given connection
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/

tptp_int32   terminateSharedMemConnection(server_block_t* pServerData, tptp_uint32 connectionID)
{
	return ( 0 ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    set up the server named pipe for listening incoming requests
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
THREAD_USER_FUNC_RET_TYPE doListening(LPVOID args) 
{
	server_block_ptr_t  pParam = (server_block_ptr_t) args ;
	TPTP_LOG_DEBUG_MSG1(pParam, "Shared memory server is running and listening at \"%s\" name.", pParam->sharedMemName) ;

	serveRequest(pParam) ;

	return ( 0 );
}


/**
 *********************************************************
 *
 * @brief
 *    process the data received from shared memory on the main global block
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
int sharedMemMainDataProcessorFunc(void *pData, int dataLen, void *pArgs)
{
	unsigned int payLoadLength = 0 ;

	server_block_ptr_t  pServerData = (server_block_ptr_t) pArgs ;
	TPTP_LOG_DEBUG_MSG1(pServerData, "sharedMemMainDataProcessorFunc: Received %d bytes.", dataLen) ;

	printThisEnvelope((tptp_basic_msg_header_ptr_t) pData) ;

	/* process the message */
	processTheMessageHeader(pServerData, pData, &payLoadLength) ;

	return 0 ;
}

/**
 *********************************************************
 *
 * @brief
 *    process the data received from shared memory block of a specific agent
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
int sharedMemAgentDataProcessorFunc(void *pData, int dataLen, void *pArgs)
{
	char *buffer = (char *) pData ;
	sendData_ptr_t pSendFunc;
	request_block_ptr_t pAgentData = (request_block_ptr_t) pArgs ;

	pAgentData->connected = 1;
	ipcCloseMem (&pAgentData->dataMemInfo);	// mark the segment to be removed
											// when all user disconnect
	pSendFunc = pAgentData->pSendFunc;

	TPTP_LOG_DEBUG_MSG3(pAgentData->pServerData, "sharedMemAgentDataProcessorFunc: Received %d bytes of data, Conn Id %d, Partner Id %d",
		dataLen, pAgentData->connectionId, pAgentData->connectionPartnerID) ;

	if (pSendFunc != NULL)
		pSendFunc(pAgentData->partner, pAgentData->connectionPartnerID, dataLen, buffer) ;
	else
		TPTP_LOG_DEBUG_MSG(pAgentData->pServerData, "sharedMemAgentDataProcessorFunc: Data path is NOT established yet.") ;

	return 0 ;
}



/**
 *********************************************************
 *
 * @brief
 *    main running thread that accepts connection,
 *    set up the environment and process it in the current thread
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
int serveRequest(server_block_ptr_t pServerData)
{
	int      rc = 0 ;

	mem_map_block_ptr_t pMemBlockInfo = &pServerData->serverMemInfo  ;

	/* initial status before the thread is running */
	pServerData->threadStatus = RUNNING ;

	while (pServerData->threadStatus == RUNNING)
	{
		/* attach to this block so that it will not be deleted */
		ipcMemOpen(pServerData->sharedMemName, pMemBlockInfo);
	
	    rc =  ipcFlushToFunc(pMemBlockInfo, sharedMemMainDataProcessorFunc, (void *) pServerData) ;
   	}

	TPTP_LOG_DEBUG_MSG(pServerData, "Shared memory server ends...") ;

	destroyMem (pMemBlockInfo);

	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    running thread that accepts data transfer from agent.
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
THREAD_USER_FUNC_RET_TYPE processDataRequest(LPVOID args) 
{
	int      rc  = 0 ;
	int 	 nconnect = CONNECT_TIMEOUT / FLUSHER_TIMEOUT;

	request_block_ptr_t pAgentData = (request_block_ptr_t) args ;
	mem_map_block_ptr_t pMemBlockInfo = &pAgentData->dataMemInfo  ;

	/* initial status before the thread is running */
	pAgentData->threadStatus = RUNNING ;
	pAgentData->connected = 0;

	while (1) {
		rc =  ipcFlushToFunc(pMemBlockInfo, sharedMemAgentDataProcessorFunc, (void *) pAgentData);
		if (rc) break;

		if (pAgentData->connected) break;	// agent disconnected
		if (--nconnect <= 0) break;			// connection timeout
	}
 
	pAgentData->threadStatus = IDLE ;

	TPTP_LOG_DEBUG_MSG(pAgentData->pServerData, "Shared memory agent ends...") ;

	ipcCloseMem(pMemBlockInfo);
	ipcMemDestroy(pMemBlockInfo);

	ipcCloseMem(&pAgentData->agentMemInfo);
	ipcMemDetach (&pAgentData->agentMemInfo);

	tableRemove(pAgentData->pServerData->connectionTable, pAgentData->connectionId);

	return ( 0 ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    Process the CONNECT_DATA request
 *
 * @note
 *    CONNECT_DATA message format
 *        - standard message header
 *        - flags (SEND_DATA or RECEIVE_DATA or both)
 *        - uuid length
 *        - the uuid itself which will be used to send back data to the agent
 *
 * @return
 *    0 - success
 *    non-zero - error occurs
 *********************************************************/
int processCONNECT_DATACall(server_block_ptr_t pServerData,  char *pMsg, int payLoadLength)
{
	int rc = 0 ;
	char  *buffer = NULL;
	int   bufferLength =0;
	BOOL  shouldAddHeader = FALSE ;
	int   uuidLength = 0 ;
	char *pCurr = NULL ;

	int connId = 0 ;
	TID threadId;
	HANDLE threadHandle ;

	request_block_ptr_t pBlk = NULL ;

	addDataConnectionEntry_ptr_t pFunc = NULL ;

	unsigned int flags = 0 ;

	char *cmd = NULL;
	int   cmdLength = 0;

	HashTable           *pTab = NULL ;

	/* set up the data block for the new request */
	pBlk = getInitRequestDataBlock(pServerData) ;

	/* read in the flags */
	pMsg = readUINTFromBuffer(pMsg, &pBlk->flags);

	/* get the assigned connection id */
	pFunc = pServerData->agentControllerDataBlk.addDataConnectionEntry ;
	pFunc(pServerData->cmo,
	        pServerData->agentControllerDataBlk.transportID,
			pBlk->flags,
			&(pBlk->connectionId)) ;

	/* save the socket and the control block away */
	/*   use the connection id as the index into the array for fast lookup */
	connId = pBlk->connectionId ;
	TPTP_LOG_DEBUG_MSG1(pServerData, "Connection ID: %d", connId) ;
 
	/* add the request in the connection table */
	pTab = pServerData->connectionTable ;
	tablePut(pTab, connId, (Entry_value_ptr_t) pBlk) ;

	pBlk->connectionType = DATA_CHANNEL ;
	TPTP_LOG_DEBUG_MSG(pServerData, "CONNECT_DATA connection request.") ;

	/* read in the length of the given uuid */
	pMsg = readUINTFromBuffer(pMsg, &uuidLength);

	/* keep the agent's shared memory name for sending this response back */
	strncpy(pBlk->uuid, pMsg, uuidLength) ;
	pBlk->uuid[uuidLength] = '\0' ;
	TPTP_LOG_DEBUG_MSG1(pServerData, "UUID: %s.", pBlk->uuid) ;

	rc = ipcMemOpen(pBlk->uuid, & pBlk->agentMemInfo) ;
	if (rc != 0) 
	{
		// Report an error, unable to open a shared memory block
		return rc;
	} 

	/* The two lines below were moved from processDataRequest
	 * to here in order to avoid a race condition on notifying
	 * the agent of the ipc memory name.  See Bugzilla 133582
	 */
	/* Create shared memory to receive data from the agent */
	rc = ipcMemCreate(pBlk->dataUUID, &pBlk->dataMemInfo, 0);

	/* listening to the agent's incoming data */
	rc = tptpStartThread(processDataRequest, (LPVOID) pBlk,
			&threadId, &threadHandle) ;
	CLOSE_THREAD_HANDLE(threadHandle);

	/* Prepare the response command */
	/* Data connection complete contains: assigned connection id, uuid length & string */
	cmdLength = sizeof(int) + sizeof(int) + strlen(pBlk->dataUUID) + 1;
	cmd = (char*) tptp_malloc(cmdLength);
	if (!cmd) return -1;
	pCurr = writeUINTToBuffer(cmd, pBlk->connectionId);
	uuidLength = strlen(pBlk->dataUUID) + 1;
	pCurr = writeUINTToBuffer(pCurr, uuidLength);
	strcpy(pCurr, pBlk->dataUUID) ;

	flags = DATA_CONNECTION_COMPLETE ;
	addBasicMsgHeader(cmd, cmdLength, &buffer, &bufferLength, flags);
	
	/* send the response */
	sendThisMessage(pBlk->pServerData, pBlk->connectionId, bufferLength, buffer, shouldAddHeader) ;

	if (cmd) tptp_free(cmd);
	if (buffer) tptp_free(buffer);

	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    handle the DISCONNECT request
 *
 *********************************************************/
int handleDISCONNECT(server_block_ptr_t pServerData, char *pMsg)
{
	mem_map_block_ptr_t pMemBlockInfo;
	
	/* locate the block */
	request_block_ptr_t pBlk = NULL ;

	tptp_uint32 connectionID ;

	/* read in the given conn id */
	pMsg = readUINTFromBuffer(pMsg, &connectionID);

	pBlk = (request_block_ptr_t) tableGet(pServerData->connectionTable, connectionID) ;
	if (pBlk == NULL) {
		return -1;
	}
	
	if (pBlk->connectionType == DATA_CHANNEL)
	{
		/* pass the command to the agent controller to be processed */
		removeDataConnectionEntry_ptr_t pDataRemoveFunc = 
			pBlk->pServerData->agentControllerDataBlk.removeDataConnectionEntry ;

		if (pDataRemoveFunc != NULL) 
			pDataRemoveFunc(pBlk->pServerData->cmo, pBlk->connectionId) ;
	}
	else
	{
		/* pass the command to the agent controller to be processed */
		removeConnectionEntry_ptr_t pRemoveFunc = 
			pServerData->agentControllerDataBlk.removeConnectionEntry ;

		if (pRemoveFunc != NULL) 
			pRemoveFunc(pServerData->cmo, pBlk->connectionId) ;
	}

	pBlk->connected = 1;
	pMemBlockInfo = &pBlk->dataMemInfo;
	ipcCloseMem(pMemBlockInfo);
	ipcStopFlusher(pMemBlockInfo);

	return 0 ;
}



/**
 *********************************************************
 *
 * @brief
 *    Extract out the message header from the given message
 *
 * @return
 *    The pointer to the message without the header
 *
 *    NULL if the message is intended to the transport layer
 *    and it has been processed and responded by the transport.
 *
 *********************************************************/
char * processTheMessageHeader(server_block_ptr_t pServerData, char *pMsg, int *pPayLoadLength)
{
	unsigned int magicNumber = 0;
	unsigned int flags = 0 ;

	/* read in the magic number */
	pMsg = readUINTFromBuffer(pMsg, &magicNumber);

	/* read in the flags */
	pMsg = readUINTFromBuffer(pMsg, &flags);

	/* read in the payload length */
	pMsg = readUINTFromBuffer(pMsg, pPayLoadLength) ;

	if ((flags & CONNECT_DATA) != 0)
	{
		TPTP_LOG_DEBUG_MSG(pServerData, "DATA_CHANNEL") ;

		/* CONNECT_DATA command. Go handle it. */
		processCONNECT_DATACall(pServerData, pMsg, *pPayLoadLength) ;

		/* TODO: processCONNECT_DATACall() needs to be able to report a failure,
		 * such as error - opening shared mem. This needs to be reported to caller.
		 */

		/* prevent it from forwarding to the AC */
		pMsg = NULL ;
	}

	else if ((flags & DISCONNECT) != 0)
	{
		handleDISCONNECT(pServerData, pMsg) ;

		/* prevent it from forwarding to the AC */
		pMsg = NULL ;
	}

	return ( pMsg ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    create and initialize the data block used to process this new request
 *
 * @return
 *    Pointer to the data block used to process this client request
 *    NULL if error
 *********************************************************/
request_block_ptr_t  getInitRequestDataBlock(server_block_ptr_t pServerData)
{
	request_block_ptr_t pRequestDataBlock = NULL ;

	/* set up the data block for each request */
	pRequestDataBlock = (request_block_ptr_t) tptp_malloc(sizeof(request_block_t)) ;

	pRequestDataBlock->pServerData = pServerData ;
	pRequestDataBlock->connectionId = 0 ;

	/* generate the unique id string */
	/* This unique id is formed from the config name combined with a counter */

	sprintf(pRequestDataBlock->dataUUID,"%s%d", pServerData->sharedMemName, acShmMemCount++);
	if (strlen(pRequestDataBlock->dataUUID) > 24) 
	{
		TPTP_LOG_ERROR_MSG1(pServerData, "Error: generated (%s) shared memory name is greater than 24", pRequestDataBlock->dataUUID);
	}

	pRequestDataBlock->pSendFunc = NULL ;

	return ( pRequestDataBlock ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    send a given message to the given destination (identified by connection id)
 *
 * @return
 *    Negative - Error
 *    0 - Success
 *    Positive - Error 
 *********************************************************/

int   sendThisMessage( server_block_t* pServerData, tptp_uint32 connectionID, tptp_uint32 cmdSize, char * pCmdBlock, BOOL shouldAddHeader)
{
	int rc = 0 ;
	char *buffer = NULL;
	int  bufferLength = 0 ;
	unsigned int flags = 0 ;
	char *pSendBuffer = NULL;

	/* locate the sender's block */
	request_block_ptr_t pBlock = 
		(request_block_ptr_t) tableGet(pServerData->connectionTable, connectionID) ;

	if (shouldAddHeader == TRUE)
	{
		/* add the header to the message if requested */
		addBasicMsgHeader(pCmdBlock, cmdSize, &buffer, &bufferLength, flags);
		pSendBuffer = buffer;
	}
	else
	{
		pSendBuffer = pCmdBlock;
		bufferLength = cmdSize;
	}

	/* sent a message to the agent */
	rc = ipcMemWrite(pSendBuffer, bufferLength, &pBlock->agentMemInfo);
	if (rc != 0)
	{
		TPTP_LOG_ERROR_MSG(pServerData, "Error: unable to send data to the agent using shared mem.");
	}
	else
	{
		printThisEnvelope((tptp_basic_msg_header_ptr_t) pSendBuffer) ;
	}

	if (buffer) tptp_free(buffer);

	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    common interface to set up the data path between source and destination
 *    (shared mem).
 *
 * @return
 *    0 - Success
 *    negative - Not supported
 *    positive - Error number
 *********************************************************/

tptp_int32   setSharedMemIncomingDataFunc( server_block_t* pServerData, tptp_uint32 connectionID, tptp_uint32 partnerID, tptp_object* partner, sendData_ptr_t newDataFunc )
{
	/* locate the block */
	request_block_ptr_t pBlock = 
		(request_block_ptr_t) tableGet(pServerData->connectionTable, connectionID) ;

	TPTP_LOG_DEBUG_MSG2(pServerData, "setIncomingDataFunc(shared memory) connectionID(%d) partnerID(%d)...",
		connectionID, partnerID) ;

	pBlock->connectionPartnerID = partnerID ;
	pBlock->partner = partner;
	pBlock->pSendFunc = newDataFunc ;

	return 0 ;
}


/**
 *********************************************************
 *
 * @brief
 *    send a given message to the given destination (identified by connection id)
 *
 * @return
 *    Negative - Error
 *    0 - Socket is closed gracefully
 *    Positive - the number of bytes that have been sent
 *********************************************************/

tptp_int32   sendSharedMemData(server_block_t* pServerData, tptp_uint32 connectionID, tptp_uint32 dataSize, tptp_string* pDataBlock)
{
	tptp_int32 rc = 0 ;

	/* locate the block */
	request_block_ptr_t pBlock = 
		(request_block_ptr_t) tableGet(pServerData->connectionTable, connectionID) ;	
	if (pBlock == NULL) return -1;

	/* sent a message to the agent */
	rc = ipcMemWrite(pDataBlock, dataSize, &pBlock->agentMemInfo);
	
	return ( rc ) ;
}


