Files
libraries/ux/ux_mon.c
Philip Smart af833c09ee Initial upload
2019-11-18 00:29:48 +00:00

1098 lines
36 KiB
C
Executable File

/******************************************************************************
* Product: # # # # # ### ######
* # # # # # # # #
* # # # # # # # #
* # # # # # ######
* # # # # # # # #
* # # # # # # # #
* ##### # # ####### ####### ### ######
*
* File: ux_mon.c
* Description: Interactive Monitor functionality. Provides a suite of
* interactive commands (HTML or Natural Language) that a user
* can issue to an executing application that incorporates
* these facilities.
*
* Version: %I%
* Dated: %D%
* Copyright: P.D. Smart, 1994-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 <http://www.gnu.org/licenses/>.
******************************************************************************/
/* Bring in system header files.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <sys/types.h>
#include <fcntl.h>
#if defined(_WIN32)
#include <winsock.h>
#include <time.h>
#include <errno.h>
#include <io.h>
#include <fstream.h>
#endif
#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX)
#include <errno.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#endif
#if defined(SOLARIS)
#include <sys/file.h>
#endif
#include <sys/timeb.h>
#include <sys/stat.h>
/* Indicate that we are a C module for any header specifics.
*/
#define UX_MONITOR_C
/* Bring in specific header files.
*/
#include "ux.h"
/* Local module variables.
*/
static ML_GLOBALS Ml;
/******************************************************************************
* Internal Library functions.
******************************************************************************/
/******************************************************************************
* Function: _ML_HTMLDefaultCB
* Description: A default handler for the HTML interpreter which basically
* sends a not-coded message to the client.
*
* Returns: Non.
******************************************************************************/
int _ML_HTMLDefaultCB( UINT nChanId, /* I: Id - xmit to client*/
UCHAR *szData, /* I: Data Buffer */
UINT nDataLen ) /* I: Length of data buf */
{
/* Local variables.
*/
UCHAR szTmpBuf[MAX_TMPBUFLEN+1];
char *szFunc = "_ML_HTMLDefaultCB";
/* Create a basic message to inform client that the command has not
* yet been implemented.
*/
sprintf(szTmpBuf, "<TITLE>Not Implemented</TITLE>"
"<H1>Not Implemented</H1>\n"
"No Handler has been implemented for the issued command. "
"Please contact the developer of this software and decide upon"
" appropriate action.\n\n\n"
"<H1>Have a Nice Day!</H1>\n\n");
/* Fire it off to the client, logging any errors as required.
*/
if(ML_Send(nChanId, szTmpBuf, strlen(szTmpBuf)) == R_FAIL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc,
"Unable to xmit negative response to client on channel (%d)",
nChanId);
}
/* All done, get out!
*/
return( R_OK );
}
/******************************************************************************
* Function: _ML_InterpretHTMLRequest
* Description: Buffer from an external client (ie. Web Browser) contains a
* an HTML request. Interpret it into a set of actions through
* comparisons and deductions.
* Returns: Non.
******************************************************************************/
UINT _ML_InterpretHTMLRequest( ML_MONLIST *spMon, /* I: Monitor Desc */
ML_CONLIST *spCon, /* I: Connection Desc */
UCHAR *szData ) /* I: Command Buffer */
{
/* Local variables.
*/
UINT nPos = 0;
UINT nTokType;
UCHAR *spTmpTok = NULL;
UCHAR szTmpBuf[MAX_TMPBUFLEN+1];
LINKLIST *spNext = NULL;
ML_COMMANDLIST *spMC = NULL;
char *szFunc = "_ML_InterpretHTMLRequest";
/* Allocate temporary working buffers.
*/
if( (spTmpTok=(UCHAR *)malloc(strlen(szData)+1)) == NULL )
{
if(spTmpTok != NULL) free(spTmpTok);
/* Get out with a failure, memory exhausted.
*/
return(R_FAIL);
}
/* Get first item in buffer.
*/
nTokType = ParseForToken(szData, &nPos, spTmpTok);
/* Get the first token from the HTML buffer and check to see if its
* within the recognised command set.
*/
for(spMC=(ML_COMMANDLIST *)StartItem(spMon->spMCHead, &spNext);
spMC != NULL; spMC=(ML_COMMANDLIST *)NextItem(&spNext))
{
/* Match?
*/
if(StrCaseCmp(spMC->szCommand, spTmpTok) == 0)
{
/* Send positive header to browser client.
*/
sprintf(szTmpBuf, "HTTP/1.0 200 OK\nServer: %s\n",
spMon->szServerName);
if(ML_Send(spCon->nChanId, szTmpBuf, strlen(szTmpBuf)) == R_FAIL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc,
"Unable to xmit positive response to client on chan (%d)",
spCon->nChanId);
} else
{
/* Client setup, call callback to actually do all the
* processing.
*/
if(spMC->nCallback != NULL)
spMC->nCallback(spCon->nChanId,&szData[nPos], spMon->nMonPort);
}
break;
}
}
/* If we got to the end of the list without a match then we have an
* unrecognised command, log it and send fail message.
*/
if(spMC == NULL)
{
/* Send negative header to browser client.
*/
sprintf(szTmpBuf, "HTTP/1.0 400 OK\nServer: %s\n\n"
"<TITLE>Not Found</TITLE><H1>%s - Not Found</H1>\n"
"The requested object does not exist on this server.\n\n",
spMon->szServerName, spMon->szServerName);
if(ML_Send(spCon->nChanId, szTmpBuf, strlen(szTmpBuf)) == R_FAIL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc,
"Unable to xmit negative response to client on channel (%d)",
spCon->nChanId);
}
}
/* Success, exit.
*/
return(R_OK);
}
/******************************************************************************
* Function: _ML_InterpretNLRequest
* Description: Buffer from an external client contains a natural language
* command request. Interpret it into a set of actions through
* comparisons.
* Returns: Non.
******************************************************************************/
UINT _ML_InterpretNLRequest( ML_MONLIST *spMon, /* I: Monitor Desc */
ML_CONLIST *spCon, /* I: Connection Desc */
UCHAR *szData ) /* I: Command Buffer */
{
/* Local variables.
*/
UINT nFound;
UINT nPos = 0;
UINT nResult;
UINT nTokType;
UCHAR *spTmpTok = NULL;
LINKLIST *spNext = NULL;
ML_COMMANDLIST *spMC = NULL;
/* Allocate temporary working buffers.
*/
if( (spTmpTok=(UCHAR *)malloc(strlen(szData)+1)) == NULL )
{
if(spTmpTok != NULL) free(spTmpTok);
/* Get out with a failure, memory exhausted.
*/
return(R_FAIL);
}
/* Get first item in buffer.
*/
nTokType = ParseForToken(szData, &nPos, spTmpTok);
/* If the first parameter is not a alpha or alphanum, then invalid,
* get out.
*/
if(nTokType != TOK_ALPHA)
{
/* Bad input, warn remote and get out.
*/
SL_BlockSendData(spCon->nChanId, "*** Input Invalid ***\n", 22);
return(R_FAIL);
}
/* Check amount of data entered, cant be greater than internal
* buffers, cos a) it would overflow, b) its meaningless.
*/
if(strlen(&szData[nPos]) >= MAX_TMPBUFLEN)
{
/* Line too long, warn remote and get out.
*/
SL_BlockSendData(spCon->nChanId, "*** Line too Long ***\n", 22);
return(R_FAIL);
}
/* Try and locate command in link list.
*/
for(nFound=FALSE, spMC=StartItem(spMon->spMCHead, &spNext);
spMC != NULL; spMC=NextItem(&spNext))
{
/* Have we located the required command.
*/
if(strncasecmp(spMC->szCommand, spTmpTok, 3) == 0 &&
spMC->nCallback != NULL)
{
nFound = TRUE;
spMC->nCallback(spCon->nChanId,&szData[nPos],strlen(&szData[nPos]));
}
}
/* If the command was not located... Send a default error message.
*/
if(nFound == FALSE)
{
SL_BlockSendData(spCon->nChanId, "*** Illegal Command ***\n", 24);
return(R_FAIL);
}
/* Exit with success.
*/
return(R_OK);
}
/******************************************************************************
* Function: _ML_MonitorCB
* Description: When an external client is issuing commands to us, the command
* data is delivered to this function for processing.
* Returns: Non.
******************************************************************************/
void _ML_MonitorCB( UINT nChanId, /* I: Channel data received on */
UCHAR *szData, /* I: Buffer containing data */
UINT nDLen ) /* I: Length of data in buffer */
{
/* Local variables.
*/
UINT nResult;
UCHAR *spTmpData = NULL;
UCHAR *spTmpTok = NULL;
UCHAR *szFunc="_ML_MonitorCB";
LINKLIST *spNext = NULL;
ML_CONLIST *spCon;
ML_MONLIST *spMon;
/* Locate the Monitor Descriptor record corresponding to this
* request.
*/
for(spMon=(ML_MONLIST *)StartItem(Ml.spMonHead, &spNext);
spMon != NULL && (spCon=(ML_CONLIST *)FindItem(spMon->spConHead, &nChanId, NULL, NULL)) == NULL;
spMon=(ML_MONLIST *)NextItem(&spNext));
/* Error condition? Without the record, carrying on is pointless.
*/
if(spMon == NULL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc,
"Unable to locate Monitor Descriptor record for ChanId (%d)",
nChanId);
return;
}
/* Allocate temporary working buffers.
*/
if( (spTmpData=(UCHAR *)malloc(nDLen+1)) == NULL )
{
if(spTmpData != NULL) free(spTmpData);
/* Get out, we must waste this request as we are in a situation where
* the machine is dying or weve reached our memory quota, either case
* it requires external rectification.
*/
return;
}
/* Copy data and add a terminator.
*/
strncpy(spTmpData, szData, nDLen);
spTmpData[nDLen] = '\0';
/* Has the application provided its own interpreter for received data
* buffers? If it has, use it, else use in-built interpreter.
*/
if(spMon->nInterpretOvr != NULL)
{
/* Pass all received data over to application handler.
*/
nResult=spMon->nInterpretOvr(spCon->nChanId, spMon->nMonPort,
spTmpData);
} else
{
/* Process data according to the service type in operation.
*/
switch(spMon->nServiceType)
{
case MON_SERVICE_HTML:
nResult=_ML_InterpretHTMLRequest(spMon, spCon, spTmpData);
/* For an HTML connection, once the request has been processed
* its use comes to an end, so close it and clean up
* resources used.
* Post a close message to the Socket Library to close and
* remove the connection.
*/
if(SL_Close(nChanId) == R_FAIL)
{
/* A problem has occurred, weve either remembered the
* wrong channel Id, or corruption is occuring.
*/
Lgr(LOG_DEBUG, szFunc,
"Cannot close socket with channel Id = %d", nChanId);
}
break;
case MON_SERVICE_NL:
nResult=_ML_InterpretNLRequest(spMon, spCon, spTmpData);
break;
default:
break;
}
}
/* Free up buffers.
*/
if(spTmpData != NULL) free(spTmpData);
if(spTmpTok != NULL) free(spTmpTok);
/* Finished, get out!
*/
return;
}
/******************************************************************************
* Function: _ML_MonitorCntrl
* Description: Interactive monitor control function. WHen a connection is
* mad or broken with an external client, this function is
* requested to handle it.
* Returns: Non.
******************************************************************************/
void _ML_MonitorCntrl( int nType, /* I: Type of callback */
... ) /* I: Argument list according to type */
{
/* Local variables.
*/
UINT nChanId;
UINT nOurPortNo;
UINT nPortNo;
ULNG lIPaddr;
UCHAR szTmpBuf[MAX_TMPBUFLEN];
UCHAR *szFunc="_ML_MonitorCntrl";
va_list pArgs;
ML_CONLIST *spCon;
ML_MONLIST *spMon;
/* 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)
{
case SLC_NEWSERVICE:
/* Extract the relevant details of the new connection.
*/
nChanId = va_arg(pArgs, UINT);
nPortNo = va_arg(pArgs, UINT);
lIPaddr = va_arg(pArgs, ULNG);
nOurPortNo = va_arg(pArgs, UINT);
/* Locate the Monitor Descriptor record corresponding to this
* Port Number. This record contains all control information and
* a list of current connections which we browse, validate and
* add to.
*/
if((spMon=(ML_MONLIST*)FindItem(Ml.spMonHead,&nOurPortNo,NULL,NULL))
== NULL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc,
"Unable to locate Monitor Descriptor record for port (%d)",
nOurPortNo);
break;
}
/* Lookup this channel ID in the list of active connections. If it
* exists, we have a ghost, log it and exit.
*/
if((ML_CONLIST *)FindItem(spMon->spConHead,&nChanId,NULL,NULL)
!= NULL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc,
"Duplicate channel ID (%d). Channel ID already active",
nChanId);
break;
}
/* Create a new connection list record for this connection.
*/
if((spCon=(ML_CONLIST *)malloc(sizeof(ML_CONLIST))) == NULL)
{
/* Weve run out of memory, log error,setup exit code and
* exit, wasting connection.
*/
Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes",
sizeof(ML_CONLIST));
break;
}
/* Populate the new record with the required data.
*/
spCon->nChanId = nChanId;
spCon->nClientPortNo = nPortNo;
spCon->lClientIPaddr = lIPaddr;
/* Tag onto end of existing list.
*/
if(AddItem(&spMon->spConHead,&spMon->spConTail,SORT_NONE,&nChanId,
NULL, NULL, spCon) == R_FAIL)
{
/* Dont modify Errno as AddItem has already set it for the
* correct error condition.
*/
free(spCon);
break;
}
/* Call the application Connect CB Handler for its own internal
* processing if one has been specified.
*/
if(spMon->nConnectCB != NULL)
spMon->nConnectCB(spCon->nChanId, spMon->nMonPort);
/* Configure the channel so that it uses Raw Mode Technology.
*/
SL_RawMode(spCon->nChanId, TRUE);
break;
case SLC_LINKDOWN:
case SLC_LINKFAIL:
/* Extract the relevant details of the new connection.
*/
nChanId = va_arg(pArgs, UINT);
nPortNo = va_arg(pArgs, UINT);
lIPaddr = va_arg(pArgs, ULNG);
nOurPortNo = va_arg(pArgs, UINT);
/* Locate the Monitor Descriptor record corresponding to this
* Port Number.
*/
if((spMon=(ML_MONLIST *)FindItem(Ml.spMonHead, &nOurPortNo,
NULL,NULL)) == NULL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc,
"Unable to locate Monitor Descriptor record for port (%d)",
nOurPortNo);
break;
}
/* Delete the record off the linked list, no longer needed.
*/
if(DelItem(&spMon->spConHead, &spMon->spConTail, NULL, &nChanId,
NULL, NULL) == R_FAIL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc,
"Connection closure on unknown channel Id (%d)", nChanId);
break;
}
/* Call the application Disconnect CB Handler for its own internal
* processing if one has been specified.
*/
if(spMon->nDisconCB != NULL)
spMon->nDisconCB(nChanId, spMon->nMonPort);
break;
default:
break;
}
/* Terminate var-arg list.
*/
va_end(pArgs);
/* Finished, get out!
*/
return;
}
/******************************************************************************
* Function: _ML_MonTerminate
* Description: An interactive monitor command to terminate us (the program).
* Author: P.D. Smart
* Returns: Non.
******************************************************************************/
int _ML_MonTerminate( UINT nChanId, /* I: Channel to Im session */
UCHAR *szBuf, /* I: Remainder of input line */
UINT nBufLen ) /* I: Length of input line */
{
/* Local variables.
*/
UCHAR szTmpBuf[MAX_TMPBUFLEN];
/* Send signoff message.
*/
sprintf(szTmpBuf, "%c", MON_MSG_TERMINATE);
SL_BlockSendData(nChanId, szTmpBuf, strlen(szTmpBuf));
#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX)
/* Sleep for a while to let the comms filter through.
*/
sleep(2);
#endif
/* Request that the process terminates.
*/
SL_PostTerminate();
/* Finished, get out!!
*/
return( R_OK );
}
/******************************************************************************
* User API functions.
******************************************************************************/
/******************************************************************************
* Function: ML_Init
* Description: Initialise all functionality to allow a remote user to
* connect with this executing program and issue commands to it.
* Returns: Non.
******************************************************************************/
int ML_Init( UINT nMonPort, /* I: Port that monitor service is on */
UINT nServiceType, /* I: Type of monitor service. ie HTML */
UCHAR *szServerName, /* I: HTTP Server Name Response */
int (*nConnectCB)(), /* I: CB on client connection */
int (*nDisconCB)(), /* I: CB on client disconnection */
int (*nInterpretOvr)()) /* I: Builtin Interpret override func */
{
/* Local variables.
*/
UCHAR szTmpBuf[MAX_TMPBUFLEN];
char *szFunc = "ML_Init";
LINKLIST *spNext;
ML_MONLIST *spMon;
/* First, some checks:
* 1: Has this port already been registered.
*/
for(spMon=(ML_MONLIST *)StartItem(Ml.spMonHead, &spNext); spMon != NULL;
spMon=(ML_MONLIST *)NextItem(&spNext))
{
/* Port already stored in linked list?
*/
if(spMon->nMonPort == nMonPort)
{
/* Log error.
*/
Lgr(LOG_DEBUG, szFunc, "Port (%d) already registered", nMonPort);
/* This port has already been registered, exit.
*/
Errno = E_EXISTS;
return(R_FAIL);
}
}
/* 2: Valid service type. NB. Could use enumerated types for compile
* time checking, but believe future ops may be limited by this.
*/
if(nServiceType != MON_SERVICE_HTML && nServiceType != MON_SERVICE_NL &&
nServiceType != MON_SERVICE_EXT )
{
/* Log error.
*/
Lgr(LOG_DEBUG, szFunc, "Unknown Service Type (%d)", nServiceType);
/* Setup error and exit.
*/
Errno = E_BADPARM;
return(R_FAIL);
}
/* 3: If the service is for an external interpreter, ensure the
* application has provided an interpreter function callback.
*/
if(nServiceType == MON_SERVICE_EXT && spMon->nInterpretOvr == NULL)
{
/* Log error.
*/
Lgr(LOG_DEBUG, szFunc, "Application requested external interpretation but failed to provide a interpretation handler.");
/* Setup error and exit.
*/
Errno = E_BADPARM;
return(R_FAIL);
}
/* Create a new record for storage of all details relating to this
* service.
*/
if((spMon=(ML_MONLIST *)malloc(sizeof(ML_MONLIST))) == NULL)
{
/* Log error and get out.
*/
Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes",
sizeof(ML_MONLIST));
Errno = E_NOMEM;
return(R_FAIL);
}
/* Did the caller provide a server name for HTTP response headers?
*/
if(szServerName != NULL && strlen(szServerName) > 0)
{
/* Allocate memory for the server name storage.
*/
if((spMon->szServerName=(UCHAR *)malloc(strlen(szServerName)+1))==NULL)
{
/* Log error and get out.
*/
Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes",
strlen(szServerName)+1);
Errno = E_NOMEM;
return(R_FAIL);
}
/* Copy the provided server name into allocated storage.
*/
strcpy(spMon->szServerName, szServerName);
}
/* Load up record with values we currenlty know about.
*/
spMon->nMonPort = nMonPort;
spMon->nServiceType = nServiceType;
spMon->nConnectCB = nConnectCB;
spMon->nDisconCB = nDisconCB;
spMon->nInterpretOvr = nInterpretOvr;
spMon->spConHead = NULL;
spMon->spConTail = NULL;
spMon->spMCHead = NULL;
spMon->spMCTail = NULL;
/* Add a comms service on the given port number. If failure occurs,
* Errno set by SL library.
*/
if(SL_AddServer(nMonPort, FALSE, _ML_MonitorCB, _ML_MonitorCntrl) == R_FAIL)
{
free(spMon);
return(R_FAIL);
}
/* Success so far... so tag record onto end of our monitor list.
*/
if(AddItem(&Ml.spMonHead, &Ml.spMonTail, SORT_NONE, &spMon->nMonPort, NULL,
NULL, spMon) == R_FAIL)
{
/* Get rid of comms server.
*/
SL_DelServer(nMonPort);
/* Free memory.
*/
free(spMon);
/* Set Errno to reflect condition and get out.
*/
Errno = E_NOMEM;
return(R_FAIL);
}
/* Add any default commands for this service...
*/
switch(nServiceType)
{
case MON_SERVICE_NL:
ML_AddMonCommand(nMonPort, MC_TERMINATE, _ML_MonTerminate);
break;
case MON_SERVICE_HTML:
ML_AddMonCommand(nMonPort, MC_HTMLGET, _ML_HTMLDefaultCB);
ML_AddMonCommand(nMonPort, MC_HTMLHEAD, _ML_HTMLDefaultCB);
ML_AddMonCommand(nMonPort, MC_HTMLPOST, _ML_HTMLDefaultCB);
ML_AddMonCommand(nMonPort, MC_HTMLPUT, _ML_HTMLDefaultCB);
ML_AddMonCommand(nMonPort, MC_HTMLDELETE, _ML_HTMLDefaultCB);
ML_AddMonCommand(nMonPort, MC_HTMLCONNECT, _ML_HTMLDefaultCB);
ML_AddMonCommand(nMonPort, MC_HTMLOPTIONS, _ML_HTMLDefaultCB);
ML_AddMonCommand(nMonPort, MC_HTMLTRACE, _ML_HTMLDefaultCB);
default:
break;
}
/* Finished, get out!
*/
return( R_OK );
}
/******************************************************************************
* Function: ML_Exit
* Description: Decommission the Monitor module ready for program termination
* or re-initialisation.
* Returns: R_OK - Exit succeeded.
* R_FAIL - Couldnt perform exit processing, see errno.
* <Errno>
******************************************************************************/
int ML_Exit( UCHAR *szErrMsg ) /* O: Error message buffer */
{
/* Local variables.
*/
int nReturn = R_OK;
LINKLIST *spNext;
ML_COMMANDLIST *spMC;
ML_MONLIST *spMon;
/* Loop, freeing up all memory used by monitor lists.
*/
for(spMon=(ML_MONLIST *)StartItem(Ml.spMonHead, &spNext); spMon != NULL;
spMon=(ML_MONLIST *)NextItem(&spNext))
{
/* Free up interactive monitoring command memory.
*/
for(spMC=(ML_COMMANDLIST *)StartItem(spMon->spMCHead, &spNext);
spMC != NULL; spMC=(ML_COMMANDLIST *)NextItem(&spNext))
{
if(spMC->szCommand != NULL)
free(spMC->szCommand);
}
/* Delete all sublists.
*/
if(spMon->spConHead != NULL)
DelList(&spMon->spConHead, &spMon->spConTail);
if(spMon->spMCHead != NULL)
DelList(&spMon->spMCHead, &spMon->spMCTail);
}
/* Free up the main global linked list.
*/
if(Ml.spMonHead != NULL)
DelList(&Ml.spMonHead, &Ml.spMonTail);
/* Free up any character buffers...
*/
/* Finished, get out!!
*/
return( nReturn );
}
/******************************************************************************
* Function: ML_Send
* Description: Transmit data to a given remote session.
* Returns: Non.
******************************************************************************/
int ML_Send( UINT nChanId, /* I: Channel to Im session */
UCHAR *szBuf, /* I: Xmit Data */
UINT nBufLen ) /* I: Length of Xmit Data */
{
/* Local variables.
*/
UINT nBufferLen = nBufLen;
UINT nLen;
UINT nPos = 0;
int nReturn;
UINT nRetry = 20;
/* If nBufLen is 0, then the data is in a null terminated string,
* so work out its length.
*/
if(nBufferLen == 0)
nBufferLen = strlen(szBuf);
/* Split the message up into packets, for faster viewing response time
* at the users end.
*/
do {
/* Work out length of packet to be xmitted.
*/
nLen = ((nBufLen-nPos) > 2000 ? 2000 : (nBufferLen-nPos));
/* Loop until we manage to send the data or our counter expires.
*/
while((nReturn=SL_SendData(nChanId, &szBuf[nPos], nLen)) == R_FAIL)
{
/* If the failure is not due to a device being busy, get out, we'll
* never be able to transmit the data.
*/
if(Errno != E_BUSY && Errno != EINTR && Errno != ENOBUFS &&
Errno != EWOULDBLOCK)
{
nRetry = 0;
break;
}
/* If our retry counter expires, then something is very busy, so
* scrap the data.
*/
if(nRetry-- == 0)
break;
/* Sleep, hopefully the device will become free.
*/
sleep(1);
}
/* Update position pointer.
*/
nPos += nLen;
} while(nPos < nBufferLen && nRetry != 0);
/* Finished, get out!!
*/
return( nReturn );
}
/******************************************************************************
* Function: ML_AddMonCommand
* Description: Add a command to the interactive monitors database of
* recognised reserved words. When a command comes in from an
* external user, if checks it against its reserved word list
* for verification and identification.
* Returns: R_OK - Command added.
* R_FAIL - Couldnt add command, see errno.
* <Errno>
******************************************************************************/
int ML_AddMonCommand( UINT nMonPort, /* I: Service Mon Port */
UCHAR *szCommand, /* I: Command in text */
int (*nCallback)()) /* I: Command callback */
{
/* Local variables.
*/
int nReturn = R_OK;
char *szFunc = "ML_AddMonCommand";
ML_COMMANDLIST *spMC;
ML_MONLIST *spMon;
/* Locate the correct record for this Monitor Port service.
*/
if((spMon=(ML_MONLIST *)FindItem(Ml.spMonHead, &nMonPort, NULL, NULL))
== NULL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc, "Service for port (%d) not registered",
nMonPort);
Errno = E_BADPARM;
return(R_FAIL);
}
/* Does this command already exist? If it does, then caller application
* just wishes to override the callback function.
*/
if((spMC=(ML_COMMANDLIST *)FindItem(spMon->spMCHead, NULL, NULL,
szCommand)) != NULL)
{
/* Update the callback, all that needs doing.
*/
spMC->nCallback = nCallback;
} else
{
/* Create record to store command details in.
*/
if((spMC = (ML_COMMANDLIST *)malloc(sizeof(ML_COMMANDLIST))) == NULL)
{
/* Log error, setup exit code and getout.
*/
Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes",
sizeof(ML_MONLIST));
Errno = E_NOMEM;
return(R_FAIL);
} else
{
/* Wash the new memory, just in case.
*/
memset((UCHAR *)spMC, '\0', sizeof(ML_COMMANDLIST));
/* Now allocate memory to store the command.
*/
if((spMC->szCommand = (UCHAR *)malloc(strlen(szCommand)+1)) == NULL)
{
Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes",
strlen(szCommand)+1);
free(spMC);
Errno = E_NOMEM;
return(R_FAIL);
}
/* Store command and callback in new memory.
*/
strcpy(spMC->szCommand, szCommand);
spMC->nCallback = nCallback;
}
/* Add to Monitor Command list.
*/
if(AddItem(&spMon->spMCHead, &spMon->spMCTail, SORT_NONE, NULL, NULL,
spMC->szCommand, spMC) == R_FAIL)
{
/* Dont modify Errno as AddItem has already set it for the
* correct error condition.
*/
free(spMC->szCommand);
free(spMC);
return(R_FAIL);
}
}
/* Finished, get out!!
*/
return( nReturn );
}
/******************************************************************************
* Function: ML_DelMonCommand
* Description: Delete a command currently active in a monitor channels
* database of recognised words.
* Returns: R_OK - Command deleted.
* R_FAIL - Couldnt delete command, see errno.
* <Errno>
******************************************************************************/
int ML_DelMonCommand( UINT nMonPort, /* I: Service Mon Port */
UCHAR *szCommand ) /* I: Command to delete */
{
/* Local variables.
*/
int nReturn = R_OK;
char *szFunc = "ML_DelMonCommand";
ML_MONLIST *spMon;
/* Locate the correct record for this Monitor Port service.
*/
if((spMon=(ML_MONLIST *)FindItem(Ml.spMonHead, &nMonPort, NULL, NULL)) == NULL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc, "Service for port (%d) not registered",
nMonPort);
Errno = E_BADPARM;
return(R_FAIL);
}
/* Attempt to delete the command. If its not found, then report an error,
* command may have been previously deleted, misspelled or not added.
*/
if(DelItem(&spMon->spMCHead, &spMon->spMCTail, NULL, NULL, NULL,
szCommand) == R_FAIL)
{
/* Log error and setup exit code.
*/
Lgr(LOG_DEBUG, szFunc, "Command (%s) couldnt be deleted, not present",
szCommand);
Errno = E_BADPARM;
nReturn = R_FAIL;
}
/* Finished, get out!!
*/
return( nReturn );
}
/******************************************************************************
* Function: ML_Broadcast
* Description: Broadcast a message to all listening monitor processes.
* Returns: R_OK - Data sent to some/all successfully.
* R_FAIL - Couldnt send to any, see Errno.
* <Errno>
******************************************************************************/
int ML_Broadcast( UCHAR *szData, /* I: Data to be sent */
UINT nDataLen ) /* I: Length of data */
{
/* Local variables.
*/
int nReturn = R_FAIL;
LINKLIST *spNext;
/* Scan list to find monitor clients.
for(spNetCon=(ML_NETCONS *)StartItem(Ml.spHead, &spNext); spNetCon != NULL;
spNetCon=(ML_NETCONS *)NextItem(&spNext))
{
*/
/* A monitor port is identified by the original server port being
* equal to that stored within the control record.
if(spNetCon->cCorS == STP_SERVER && spNetCon->nServerPortNo != 0 &&
spNetCon->nOurPortNo == Ml.nMonPort)
{
if(SL_BlockSendData(spNetCon->nChanId, szData, nDataLen) == R_OK)
nReturn = R_OK;
}
}
*/
/* Finished, get out.
*/
return(nReturn);
}