/******************************************************************************
* Product: # # ###### ##### # ### ######
* ## ## # # # # # # # #
* # # # # # # # # # # #
* # # # # # # # # ######
* # # # # # # # # #
* # # # # # # # # # #
* # # ###### ##### ####### ####### ### ######
*
* File: mdc_server.c
* Description: Meta Data Communications API providing all functionality
* for server processes within the Virtual Data Warehouse
* project design.
*
* Version: %I%
* Dated: %D%
* Copyright: P.D. Smart, 1996-2019.
*
* History: 1.0 - Initial Release.
*
******************************************************************************
* This source file is free software: you can redistribute it and#or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This source file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
******************************************************************************/
/* Bring in system header files.
*/
#include
#include
#include
#include
#include
#include
#include
#undef R_OK
/* Indicate that we are a C module for any header specifics.
*/
#define MDC_SERVER_C
/* Bring in local specific header files.
*/
#include "mdc.h"
#include "mdc_common.h"
/******************************************************************************
* Function: _MDC_SendACK
* Description: Function to send an acknowledge to the client in response to
* a data block received correctly or a request processed
* successfully.
*
* Returns: MDC_FAIL- Couldnt transmit an ACK message to the client.
* MDC_OK - ACK sent successfully.
******************************************************************************/
int _MDC_SendACK( void )
{
/* Local variables.
*/
int nReturn = MDC_OK;
UCHAR szAckBuf[2];
UCHAR *szFunc = "_MDC_SendACK";
/* Make sure that we have a valid channel connection in case of rogue
* program code.
*/
if( MDC.nClientChanId != 0 )
{
/* Build up the message to transmit.
*/
sprintf(szAckBuf, "%c", MDC_ACK);
/* Try and transmit it.
*/
if(SL_BlockSendData(MDC.nClientChanId, szAckBuf, 1) == R_FAIL)
{
/* Log a message as this condition shouldnt occur.
*/
Lgr(LOG_ALERT, szFunc, "Couldnt transmit an ACK packet");
/* Set exit code to indicate failure.
*/
nReturn = MDC_FAIL;
}
} else
{
/* Log a message as this condition shouldnt not occur!
*/
Lgr(LOG_DEBUG, szFunc,
"Trying to transmit an ACK to an unknown client!");
/* Set exit code to indicate failure.
*/
nReturn = MDC_FAIL;
}
/* All finished, exit with result code.
*/
return(nReturn);
}
/******************************************************************************
* Function: _MDC_SendNAK
* Description: Function to send a negative acknowledge to the client in
* response to a data block which arrived incorrectly or a
* request which couldnt be processed successfully.
*
* Returns: MDC_FAIL- Couldnt transmit a NAK message to the client.
* MDC_OK - NAK sent successfully.
******************************************************************************/
int _MDC_SendNAK( UCHAR *szErrMsg ) /* I: Error msg to send with NAK */
{
/* Local variables.
*/
UINT nErrLen;
int nReturn = MDC_OK;
UINT nXmitLen;
UCHAR *psnzCmpBuf;
UCHAR *psnzTmpBuf;
UCHAR *szFunc = "_MDC_SendNAK";
/* Make sure that we have a valid channel connection in case of rogue
* program code.
*/
if( MDC.nClientChanId != 0 )
{
/* Calculate length of error message buffer for use in later
* memory manipulations.
*/
nErrLen = strlen(szErrMsg);
/* Allocate enough memory to hold a message id and the data prior
* to transmission.
*/
if((psnzTmpBuf=(UCHAR *)malloc(nErrLen+2)) == NULL)
{
/* Log a message as this condition shouldnt occur.
*/
Lgr(LOG_DEBUG, szFunc, "Couldnt allocate (%d) bytes memory",
nErrLen+2);
/* Set exit code to indicate failure.
*/
nReturn = MDC_FAIL;
} else
{
/* Log the message if we are in debug mode.
*/
Lgr(LOG_DEBUG, szFunc, "%s", szErrMsg);
/* Build up the message to transmit.
*/
*psnzTmpBuf = (UCHAR)MDC_NAK;
memcpy(psnzTmpBuf+1, szErrMsg, nErrLen);
/* Compress it to save on transmission overheads.
*/
nXmitLen=nErrLen+1;
if((psnzCmpBuf=Compress(psnzTmpBuf, &nXmitLen)) != NULL)
{
/* Free up memory we used to store the original message.
*/
if(psnzCmpBuf != psnzTmpBuf)
{
free(psnzTmpBuf);
/* Make our temporary pointer point to the new compressed
* memory block.
*/
psnzTmpBuf = psnzCmpBuf;
}
}
/* Try and transmit it.
*/
if(SL_BlockSendData(MDC.nClientChanId,psnzTmpBuf,nXmitLen) ==R_FAIL)
{
/* Log a message as this condition shouldnt occur.
*/
Lgr(LOG_ALERT, szFunc, "Couldnt transmit NAK message");
/* Set exit code to indicate failure.
*/
nReturn = MDC_FAIL;
}
/* Free up used memory, no longer needed.
*/
free(psnzTmpBuf);
}
} else
{
/* Log a message as this condition shouldnt not occur!
*/
Lgr(LOG_DEBUG, szFunc,
"Trying to transmit a NAK to an unknown client!");
/* Set exit code to indicate failure.
*/
nReturn = MDC_FAIL;
}
/* All finished, exit with success.
*/
return(nReturn);
}
/******************************************************************************
* Function: _MDC_ServerCntlCB
* Description: A function to handle any communications control callbacks
* that are generated as a result of MDC_Server being executed.
*
* Returns: No Returns.
******************************************************************************/
void _MDC_ServerCntlCB( int nType, /* I: Type of callback */
... ) /* I: Arg list according to type */
{
/* Local variables.
*/
UINT nChanId;
UINT nPortNo;
ULNG lIPaddr;
va_list pArgs;
UCHAR *szFunc = "_MDC_ServerCntlCB";
/* Start var-arg list by making pArgs point to first arg in list.
*/
va_start(pArgs, nType);
/* What type of callback is it...?
*/
switch(nType)
{
/* A new connection has arrived.
*/
case SLC_NEWSERVICE:
/* Extract var args which indicate who the client is.
*/
nChanId = va_arg(pArgs, UINT);
nPortNo = va_arg(pArgs, UINT);
lIPaddr = va_arg(pArgs, ULNG);
/* Log a message to indicate who the client is.
*/
Lgr(LOG_DEBUG, szFunc,
"New service: PPID=%d, PID=%d, Chan=%d, Port=%d, IPaddr=%s",
getppid(), getpid(), nChanId, nPortNo, SL_HostIPtoString(lIPaddr));
/* Store the Channel Id of the client so that it can be used
* for async communications.
*/
MDC.nClientChanId = nChanId;
break;
/* Given connection has become temporarily unavailable.
*/
case SLC_LINKDOWN:
/* Extract var args which indicate who the client is.
*/
nChanId = va_arg(pArgs, UINT);
nPortNo = va_arg(pArgs, UINT);
lIPaddr = va_arg(pArgs, ULNG);
/* Log a message to indicate that the link has gone down.
*/
Lgr(LOG_DEBUG, szFunc,
"Link temporarily down, Chan=%d, Port=%d, IPaddr=%s",
nChanId, nPortNo, SL_HostIPtoString(lIPaddr));
break;
/* Given connection has died.
*/
case SLC_LINKFAIL:
/* Extract var args which indicate who the client is.
*/
nChanId = va_arg(pArgs, UINT);
nPortNo = va_arg(pArgs, UINT);
lIPaddr = va_arg(pArgs, ULNG);
/* Post a signal to indicate that we are about to shut down.
*/
if(MDC.nCloseDown == FALSE)
{
/* Call out of band processing mechanism.
*/
MDC.fCntrlCB(MDC_EXIT);
/* Setup exit flag so that we can terminate, as failure of
* our main connection port means we should exit.
*/
MDC.nCloseDown = TRUE;
}
/* Log a message to indicate that the link has gone down.
*/
Lgr(LOG_DEBUG, szFunc,
"Link closed or failed, Chan=%d, Port=%d, IPaddr=%s",
nChanId, nPortNo, SL_HostIPtoString(lIPaddr));
break;
default:
/* Log a message as this condition shouldnt occur.
*/
Lgr(LOG_DEBUG, szFunc, "Unrecognised message type (%d)", nType);
break;
}
/* Terminate var-arg list.
*/
va_end(pArgs);
/* Return to caller.
*/
return;
}
/******************************************************************************
* Function: _MDC_ServerDataCB
* Description: A function to handle any data callbacks that are generated as
* a result of data arriving during an MDC_Server execution.
*
* Returns: No Returns.
******************************************************************************/
void _MDC_ServerDataCB( UINT nChanId, /* I: Channel data rcv on*/
UCHAR *szData, /* I: Rcvd data */
UINT nDataLen ) /* I: Rcvd data length */
{
/* Local variables.
*/
UINT nDLen = nDataLen;
FIFO *psFifo;
UCHAR *szFunc = "_MDC_ServerDataCB";
/* Special case processing for out of bands message. Normally messages
* are queued in a FIFO awaiting processing, but certain messages must be
* acted upon as soon as they arrive.
*/
if(nDataLen == 1)
{
/* Process according to type of message.
*/
switch(szData[0])
{
/* Special message which only has meaning when the daemon is
* returning data to the caller. Its actions are to basically
* force the driver to stop sending data and cancel all further
* data that may be awaiting transmission.
*/
case MDC_ABORT:
/* Initiate abort mechanism.
*/
MDC.fCntrlCB(MDC_ABORT);
return;
case MDC_EXIT:
/* Initiate closedown mechanism.
*/
MDC.fCntrlCB(MDC_EXIT);
MDC.nCloseDown = TRUE;
return;
default:
break;
}
}
/* Allocate memory to store a FIFO carrier. This FIFO carrier is then
* populated with the FIFO data below.
*/
if( (psFifo=(FIFO *)malloc(sizeof(FIFO))) == NULL )
{
/* Log a message if needed.
*/
Lgr(LOG_DEBUG, szFunc, "Memory exhausted, couldnt create FIFO carrier");
/* Send a NAK to client to indicate that we've run out of memory.
*/
if( _MDC_SendNAK("Memory exhausted on server, packet rejected (1)")
== MDC_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_ALERT, szFunc,
"Couldnt send a NAK message, Houston we have problems!!");
}
/* Get out, nothing more can be done.
*/
return;
}
/* Data block arrives in a compressed format, so uncompress prior to
* placing it into the incoming FIFO list.
*/
if( (psFifo->pszData=Decompress(szData, &nDLen)) == NULL )
{
/* Log a message if needed.
*/
Lgr(LOG_DEBUG, szFunc, "Couldnt decompress buffer, Chan (%d), Len (%d)",
nChanId, nDLen);
/* Send a NAK to client to indicate that the buffer sent cant be
* processed.
*/
if( _MDC_SendNAK("Couldnt decompress buffer, memory problems!")
== MDC_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_ALERT, szFunc,
"Couldnt send a NAK message, Houston we have problems!!");
}
/* Free up used resources and get out as nothing more can be done.
*/
free(psFifo);
return;
}
/* Complete the FIFO carrier information set.
*/
psFifo->nDataLen = nDLen;
/* Log message as to what has been received, may help track bugs.
*/
Lgr(LOG_DEBUG, szFunc,
"Message received: Data=%s, Len=%d", psFifo->pszData, psFifo->nDataLen);
/* Add buffer to the end of the incoming FIFO list.
*/
if( AddItem(&MDC.spIFifoHead, &MDC.spIFifoTail, SORT_NONE, NULL, NULL, NULL,
psFifo) == R_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_DEBUG, szFunc,
"Memory exhausted, couldnt add packet onto the FIFO link list");
/* Send a NAK to client to indicate that we've run out of memory.
*/
if(_MDC_SendNAK("Memory exhausted on server, packet rejected (2)")
== MDC_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_ALERT, szFunc,
"Couldnt send a NAK message, Houston we have problems!!");
}
/* Free up used resources and get out as nothing more can be done.
*/
free(psFifo->pszData);
free(psFifo);
return;
}
/* Return to caller.
*/
return;
}
/******************************************************************************
* Function: MDC_Server
* Description: Entry point into the Meta Data Communications for a server
* process. This function initialises all communications etc
* and then runs the given user callback to perform any required
* actions.
*
* Returns: MDC_FAIL- Function terminated due to a critical error, see
* Errno for exact reason code.
* MDC_OK - Function completed successfully without error.
******************************************************************************/
int MDC_Server( UINT *nPortNo, /* I: TCP/IP port number */
UCHAR *szService, /* I: Name of TCP/IP Service */
int (*fLinkDataCB) /* I: User function callback*/
(UCHAR *, int, UCHAR *),
void (*fControlCB)(UCHAR) /* I: User control callback */
)
{
/* Local variables.
*/
FIFO *psFifo;
LINKLIST *psNext;
UINT nServicePort;
static int nInitialised = FALSE;
int nReturn;
UCHAR *szFunc = "MDC_Server";
/* Check to see that we are not being called twice... some people may
* be idiotic.
*/
if( nInitialised == TRUE )
{
/* Log a message if needed.
*/
Lgr(LOG_DEBUG, szFunc, "Attempt to initialise function more than once");
/* Exit as nothing more can be done.
*/
return(MDC_FAIL);
}
/* Initialise the library, exiting if we fail.
*/
if( (nReturn=_MDC_Init()) == MDC_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_DEBUG, szFunc, "Failed to initialise MDC Library");
/* Exit as nothing more can be done.
*/
return(nReturn);
}
/* Do we need to work out the TCP port number by looking in /etc/services?
*/
if( nPortNo == NULL )
{
/* Work out the port number by a lookup on the given service name.
*/
if( szService == NULL ||
SL_GetService(szService, &nServicePort) == R_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_ALERT, szFunc,
"Service (%s) does not exist in /etc/services", szService);
/* Exit as nothing more can be achieved.
*/
return(MDC_FAIL);
}
} else
{
/* Store the port number provided into a common variable for the
* add server command.
*/
nServicePort = *nPortNo;
}
/* Add a service port so that we can accept incoming TCP connections.
*/
if( SL_AddServer(nServicePort, TRUE, _MDC_ServerDataCB, _MDC_ServerCntlCB)
== R_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_ALERT, szFunc, "Couldnt add service on port %d", nServicePort);
/* Exit, nothing more that can be done.
*/
return(MDC_FAIL);
}
/* Save the control callback within MDC structure so out of band
* control messages can call the function directly.
*/
MDC.fCntrlCB = fControlCB;
/* Where initialised, so set the flag to prevent further re-entry.
*/
nInitialised = TRUE;
ML_Init(8080, MON_SERVICE_HTML, "MDC Server/2.2.aaa", NULL, NULL, NULL);
/* Now enter a tight loop, performing CPU scheduling in a non-preemptive
* fashion and handling events as and when they occur.
*/
do {
/* Call the UX scheduler to ensure comms events occur.
*/
SL_Poll(DEF_POLLTIME);
/* Any packets on the incoming FIFO? If there are, then grab the
* first one.
*/
if( (psFifo=StartItem(MDC.spIFifoHead, &psNext)) != NULL )
{
/* Delete the entry off the FIFO before working with it to
* guarantee that we never call the user twice with the same
* data.
*/
if(DelItem(&MDC.spIFifoHead, &MDC.spIFifoTail, psFifo, NULL, NULL,
NULL) == R_FAIL)
{
/* Log a message if needed.
*/
Lgr(LOG_DEBUG, szFunc,
"Couldnt delete first FIFO entry, will retry later..");
} else
{
/* OK, weve got the data and we know its no longer on the FIFO,
* so lets call the users callback with this data.
*/
MDC.szErrMsg[0] = '\0';
if(fLinkDataCB(psFifo->pszData, psFifo->nDataLen, MDC.szErrMsg)
== MDC_FAIL)
{
/* The callback failed, need to send a NAK to the client
* plus the provided error message.
*/
if( _MDC_SendNAK(MDC.szErrMsg) == MDC_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_ALERT, szFunc,
"Couldnt send a NAK message, we have problems!!");
}
} else
{
/* The callback succeeded, so send out an ACK to the client
* to let him continue on his merry way.
*/
if( _MDC_SendACK() == MDC_FAIL )
{
/* Log a message to indicate problem.
*/
Lgr(LOG_ALERT, szFunc,
"Couldnt send an ACK to the client...");
}
}
}
}
} while( MDC.nCloseDown == FALSE );
/* Where exitting cleanly, so toggle flag so that a new entry can
* succeed.
*/
nInitialised = FALSE;
/* Return result to caller.
*/
return(MDC_OK);
}
/******************************************************************************
* Function: MDC_ReturnData
* Description: Function called by user code to return any required data
* to a connected client. This function can only be called
* in response to a callback 'from the MDC layer to the user
* code' which has provided data.
*
* Returns: MDC_FAIL- An error occurred in transmitting the given data
* block to the client process, see Errno for exact
* reason code.
* MDC_OK - Data packet was transmitted successfully.
******************************************************************************/
int MDC_ReturnData( UCHAR *snzDataBuf, /* I: Data to return */
int nDataLen /* I: Length of data */
)
{
/* Local variables.
*/
int nReturn = MDC_OK;
UINT nXmitLen;
UCHAR *psnzCmpBuf;
UCHAR *psnzTmpBuf;
UCHAR *szFunc = "MDC_ReturnData";
/* Make sure that we have a valid channel connection in case of rogue
* program code.
*/
if( MDC.nClientChanId != 0 )
{
/* Allocate enough memory to hold a message id and the data prior
* to transmission.
*/
if((psnzTmpBuf=(UCHAR *)malloc(nDataLen+1)) == NULL)
{
/* Log a message as this condition shouldnt occur.
*/
Lgr(LOG_DEBUG, szFunc, "Couldnt allocate (%d) bytes memory",
nDataLen+1);
/* Set exit code to indicate failure.
*/
nReturn = MDC_FAIL;
} else
{
/* Build up the message to transmit.
*/
*psnzTmpBuf = (UCHAR)MDC_DATA;
memcpy(psnzTmpBuf+1, snzDataBuf, nDataLen);
/* Compress it to save on transmission overheads.
*/
nXmitLen=nDataLen+1;
if((psnzCmpBuf=Compress(psnzTmpBuf, &nXmitLen)) != NULL)
{
/* Free up memory we used to store the original message.
*/
if(psnzCmpBuf != psnzTmpBuf)
{
free(psnzTmpBuf);
/* Make our temporary pointer point to the new compressed
* memory block.
*/
psnzTmpBuf = psnzCmpBuf;
}
}
/* Try and transmit the new buffer.
*/
if(SL_BlockSendData(MDC.nClientChanId,psnzTmpBuf,nXmitLen) ==R_FAIL)
{
/* Log a message as this condition shouldnt occur.
*/
Lgr(LOG_ALERT, szFunc, "Couldnt transmit data packet");
/* Set exit code to indicate failure.
*/
nReturn = MDC_FAIL;
}
/* Free up used memory, no longer needed.
*/
free(psnzTmpBuf);
}
} else
{
/* Log a message as this condition shouldnt not occur!
*/
Lgr(LOG_DEBUG, szFunc,
"Trying to transmit a data packet to an unknown client!");
/* Set exit code to indicate failure.
*/
nReturn = MDC_FAIL;
}
/* Return result to caller.
*/
return(nReturn);
}
/******************************************************************************
* Function: MDC_TimerCB
* Description: Function to allow user code to register a callback event
* which is activated upon a timer expiring. The user provides
* the frequency and a function to callback and the MDC
* schedules it.
*
* Returns: No Return Values.
******************************************************************************/
int MDC_TimerCB( ULNG lTimePeriod, /* I: CB Time in Millseconds */
UINT nEnable, /* I: Enable(TRUE)/Dis(FALSE) CB*/
UINT nAstable, /* I: Astable(TRUE) or Mono CB */
void (*fTimerCB)(void)/* I: Function to call back */
)
{
/* Local variables.
*/
int nReturn;
int nTimerMode;
UCHAR *szFunc = "MDC_TimerCB";
/* Initialise the library, if needed.
*/
if( (nReturn=_MDC_Init()) == MDC_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_DEBUG, szFunc, "Failed to initialise MDC Library");
/* Exit as nothing more can be done.
*/
return(nReturn);
}
/* Work out the UX timer mode with given parameters.
*/
if( nEnable == TRUE )
{
if( nAstable == TRUE )
{
nTimerMode = TCB_ASTABLE;
} else
{
nTimerMode = TCB_FLIPFLOP;
}
} else
{
nTimerMode = TCB_OFF;
}
/* Call UX to add the timed event.
*/
if( SL_AddTimerCB( lTimePeriod, nTimerMode, 0, fTimerCB ) == R_FAIL )
{
/* Log a message if needed.
*/
Lgr(LOG_DEBUG, szFunc, "Failed to add timer CB, Time=%ld, Mode=%d",
lTimePeriod, nTimerMode );
/* Exit as nothing more can be done.
*/
return(MDC_FAIL);
}
/* Return result to caller.
*/
return(MDC_OK);
}