commit af833c09ee9bc5d5473e038dfa33c08d5060b53b Author: Philip Smart Date: Mon Nov 18 00:29:48 2019 +0000 Initial upload diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ab0de9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +.metadata +.dm +.gradle +/Releases +/.nb-gradle/ +*.bin +*.dmp +*.elf +*.lss +*.map +*.rpt +*.srec +*.swp +*.zpu +*.log +*.done +*.smsg +*.summary +*.jdi +*.pin +*.out.sdc +*.sof +*.sld +*.rbf +*.qws +*.sav +*.pof +*.qdf +*.srf +*.o +ux/SCCS/ +ux/1lib/ +ux/4lib/ +ux/5lib/ +ux_test/SCCS/ +ux_test/1lib/ +ux_test/4lib/ +ux_test/5lib/ +MDC/SCCS/ +MDC/1lib/ +MDC/4lib/ +MDC/5lib/ +SDD/SCCS/ +SDD/1lib/ +SDD/4lib/ +SDD/5lib/ +VDW/SCCS/ +VDW/1lib/ +VDW/4lib/ +VDW/5lib/ +VDW/odbclib/ +VDW/test/ +KIIL/ diff --git a/MDC/Makefile b/MDC/Makefile new file mode 100755 index 0000000..df76ff3 --- /dev/null +++ b/MDC/Makefile @@ -0,0 +1,144 @@ +#****************************************************************************** +#* Product: # # ###### ##### # ### ###### +#* ## ## # # # # # # # # +#* # # # # # # # # # # # +#* # # # # # # # # ###### +#* # # # # # # # # # +#* # # # # # # # # # # +#* # # ###### ##### ####### ####### ### ###### +#* +#* File: Makefile +#* Description: Build description file for the virtual data warehouse +#* Meta Data Communications API library. +#* 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 . +#****************************************************************************** +TITLE = "Meta Data Communications API Library" +COPYRIGHT = "(C) P.D.Smart, %D%, Ver %I%" +PROJ = +PURIFY = #purify +PROJPATH = /dvlp/MDC_LIB +GNUINCLUDE = #-I/apps/gnu/$(ARCH)/include +UXINCLUDE = -I../ux +INCLUDEDIR = -I. $(UXINCLUDE) $(GNUINCLUDE) +1DEBUGFLAGS = -g #-DMDC_DEBUG #-E +4DEBUGFLAGS = -g #-DMDC_DEBUG #-E +5DEBUGFLAGS = -g #-DMDC_DEBUG #-E +1OPTIMIZEFLAGS = #-O2 +4OPTIMIZEFLAGS = #-O2 +5OPTIMIZEFLAGS = #-O2 +1OPTIONFLAGS = -D${OS} #-ansi -Wall +4OPTIONFLAGS = -D${OS} #-ansi -Wall +5OPTIONFLAGS = -D${OS} -D_REENTRANT #-ansi -Wall +CFLAGS = $(${OSVER}DEBUGFLAGS) $(${OSVER}OPTIMIZEFLAGS) \ + $(${OSVER}OPTIONFLAGS) +LDFLAGS = -static +SYBLIBS = -L/apps/sybase/lib -lsybdb +UXLIBS = -L../ux/${OSVER}lib -lux +LIBS = $(UXLIBS) -lm +SCCSFLAGS = -d$(PROJPATH) +SCCSGETFLAGS = + +ifeq ($(ZPU_BUILD),) +BASE = +else +BASE = zpu-elf- +endif + +CC = $(BASE)gcc +LD = $(BASE)gcc +AS = $(BASE)as +AR = $(BASE)ar +CP = $(BASE)objcopy +DUMP = $(BASE)objdump +RANLIB = $(BASE)ranlib + +# Suffixes where interested in for this project. +# +.SUFFIXES: +.SUFFIXES: .o .c .h + +# Our way of making an object file. +# +.c.o: + $(PURIFY) $(CC) $(INCLUDEDIR) $(CFLAGS) -c $< + +# All, ie: all programs to be built +# +all: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Use 'build' command to make LIBMDC library." + +libmdc: Begin \ + libmdc.a \ + End + +# How to clean up the directory... make it look pretty! +# +clean: Begin \ + DoClean \ + End + +# How to perform an installation of the resultant software. +# +install: Begin \ + DoInstall \ + End + +# +# Pre-make start sequence. +# +Begin: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Operation commencing @ `date`" + @echo + +# +# Post-make completion sequence. +# +End: + @echo + @echo "Completed @ `date`" + +# Perform all cleanup operations to ensure future builds occur +# with completeness. +# +DoClean: + rm -f *.o *.bak *.a *.BAK *.sav core + +# Perform installation of software as per spec. +# +DoInstall: + +# Build the virtual data warehouse Meta Data Communications +# API library. +# +libmdc.a: mdc_common.o mdc_server.o mdc_client.o + $(AR) rcv libmdc.a mdc_common.o mdc_server.o mdc_client.o + +mdc_client.o: mdc.h mdc_common.h + +mdc_server.o: mdc.h mdc_common.h + +mdc_common.o: mdc.h mdc_common.h diff --git a/MDC/build b/MDC/build new file mode 100755 index 0000000..b8556e4 --- /dev/null +++ b/MDC/build @@ -0,0 +1,182 @@ +#!/bin/csh +#****************************************************************************** +#* Product: # # ###### ##### # ### ###### +#* ## ## # # # # # # # # +#* # # # # # # # # # # # +#* # # # # # # # # ###### +#* # # # # # # # # # +#* # # # # # # # # # # +#* # # ###### ##### ####### ####### ### ###### +#* +#* File: build +#* Description: Build description file for the virtual data warehouse +#* Meta Data Communications API library. +#* 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 . +#****************************************************************************** + +# Work out architecture we are compiling on.. +# +setenv ARCH `uname -s` +setenv ARCH ${ARCH}`uname -r | cut -c1` + +# Work out arguments and decide on actions from there. +# +if( $#argv > 0 ) then + + switch("$argv[1]") + + case "save": + make -f Makefile clean + sccs delta SCCS + exit 0 + + case "install": + echo "Installing libraries and header files." + echo -n "Please enter 'vdwd' " + su - vdwd <. + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef MDC_H +#define MDC_H + +/* Bring in declaration of all UX functions. +*/ +#include + +/* Definitions for maxims etc. +*/ +#define MAX_DBNAMELEN 20 /* Database Name length */ +#define MAX_ERRMSGLEN 1024 /* Length of an error msg */ +#define MAX_FTPOPTIONLEN 20 /* Length of an option string for FTP */ +#define MAX_PASSWORDLEN 20 /* Password length */ +#define MAX_SERVERNAMELEN 20 /* Server Name length */ +#define MAX_USERNAMELEN 20 /* User Name length */ + +/* Definitions for Function Returns +*/ +#define MDC_OK 0 /* Return OK */ +#define MDC_FAIL -1 /* Return FAIL */ +#define MDC_NODAEMON -2 /* No daemon at destination */ +#define MDC_SERVICENAK -3 /* Daemon cannot provide the req service */ + /* or bad parameters provided */ +#define MDC_BADCONTEXT -4 /* Attempting to perform action in the */ + /* wrong context */ +#define MDC_SNDREQNAK -5 /* NAK received for a Send Request */ + +/* Define MDC packet type definitions. +*/ +#define MDC_ACK 'A' /* Positive comms reply */ +#define MDC_ABORT 'B' /* Abort command packet */ +#define MDC_CHANGE 'C' /* Change service command */ +#define MDC_DATA 'D' /* Data packet */ +#define MDC_EXIT 'E' /* Exit command packet */ +#define MDC_INIT 'I' /* Service Initialisation request */ +#define MDC_NAK 'N' /* Negative comms reply */ +#define MDC_PREQ 'P' /* Process request command */ + +/* Service types +*/ +#define SRV_FTPX 'F' /* FTP driver */ +#define SRV_JAVA 'J' /* JAVA code execution */ +#define SRV_ODBC 'O' /* ODBC access */ +#define SRV_SCMD 'C' /* System command execution driver */ +#define SRV_SYBASE 'S' /* Sybase database access */ +#define SRV_AUPL 'A' /* Audio Player driver */ + +/* Define error return codes which are embedded into returned error messages + * for the user to decipher. +*/ +#define MDC_EMSG_MEMORY "M0000" + +/* Service details structures for Sybase & ODBC. +*/ +typedef struct SybaseServInfo { + UCHAR szUser[MAX_USERNAMELEN]; /* User Name */ + UCHAR szPassword[MAX_PASSWORDLEN]; /* Password */ + UCHAR szServer[MAX_SERVERNAMELEN]; /* Server name */ + UCHAR szDatabase[MAX_DBNAMELEN]; /* Database name */ +} SERV_INFO; + +/* Service detail structures for FTP. +*/ +typedef struct FTPServInfo { + UCHAR szServer[MAX_SERVERNAMELEN]; /* Name of FTP server */ + UCHAR szUser[MAX_USERNAMELEN]; /* User on FTP server */ + UCHAR szPassword[MAX_PASSWORDLEN]; /* Pwd on FTP server */ +} FTPX_INFO; + +typedef struct ServiceDetails { + UCHAR cServiceType; /* Type of service identifier */ + + /* Union of the different types of service structures for the different + * types of drivers. + */ + union { + /* structure for each service type + */ + SERV_INFO sSybaseInfo; + SERV_INFO sODBCInfo; + FTPX_INFO sFTPInfo; + } uServiceInfo; +} SERVICEDETAILS; + +/* Prototypes externally visible for MDC Client API. +*/ +int MDC_Start(void); +int MDC_End(void); +int MDC_SetTimeout(UCHAR *, UINT, UCHAR *); +int MDC_CreateService(UCHAR *, UINT *, SERVICEDETAILS *); +int MDC_ChangeService(UINT, SERVICEDETAILS *); +int MDC_CloseService(UINT); +int MDC_SendRequest(UINT, UCHAR *, UINT, void (*) (UINT, UCHAR *, UINT)); +int MDC_GetResult(UINT, UCHAR **); +int MDC_GetStatus(UINT, UINT *); + +/* Prototypes externally visible for MDC Server API. +*/ +int MDC_Server( UINT *, UCHAR *, int (*)(UCHAR *, int, UCHAR *), void (*)( UCHAR ) ); +int MDC_ReturnData( UCHAR *, int ); +int MDC_TimerCB( ULNG, UINT, UINT, void (*)(void) ); + +#endif /* MDC_H */ diff --git a/MDC/mdc_client.c b/MDC/mdc_client.c new file mode 100755 index 0000000..9934f29 --- /dev/null +++ b/MDC/mdc_client.c @@ -0,0 +1,1466 @@ +/****************************************************************************** + * Product: # # ###### ##### # ### ###### + * ## ## # # # # # # # # + * # # # # # # # # # # # + * # # # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * # # ###### ##### ####### ####### ### ###### + * + * File: mdc_client.c + * Description: MDC Generic communications routines. + * + * Version: %I% + * Dated: %D% + * Copyright: P. 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. +*/ +#if defined(SOLARIS) +#include +#endif +#include +#include +#include +#include +#include + +/* Bring in Unix Library headers for TCP/IP communications. +*/ +#include + +/* Indicate that we are a C module for any header specifics. +*/ +#define MDC_CLIENT_C + +/* Bring in mdc header files. +*/ +#include "mdc.h" +#include "mdc_common.h" + +/****************************************************************************** + * Function: _MDC_DataCB + * Description: This function is called back when data is received on any of + * the service connections + * Returns: void + ******************************************************************************/ +void _MDC_DataCB(UINT nChanId, UCHAR *szData, UINT nDataLen) +{ + UCHAR *szFunc = "_MDC_DataCB"; + UCHAR *szDeComData; /* pointer to decompressed data buffer */ + CHSTATE eChanSt; + void (*pUserCB) (UINT, UCHAR *, UINT); + UCHAR *pszNAKErrStr; /* pointer to Error string in */ + /* channel status structure */ + + /* Decompress + */ + szDeComData = Decompress(szData, &nDataLen); + if (szDeComData == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Cannot malloc"); + return; + } + + /* Check if the data is a service request reply + */ + if (MDC.nPendSRChanId > 0 && nChanId == MDC.nPendSRChanId) + { + /* Reply received for service request + */ + MDC.cReplyType = * ((char *) szDeComData); + if (MDC.cReplyType == MDC_NAK) + { + Lgr(LOG_DEBUG, szFunc, "NAK received for service request"); + _MDC_PrintErrMsg((szDeComData + 1), (nDataLen - 1)); + } + + MDC.nPendSRChanId = 0; + + if (szDeComData != szData) + free(szDeComData); + + return; + } + + /* This is User Data, an ACK or a NAK + Get channel state + */ + if (_MDC_GetChState(nChanId, &eChanSt) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "No status information for channel ID %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + switch(* ((char *) szDeComData)) + { + case MDC_DATA: + if (eChanSt != IN_SEND_REQUEST) + { + Lgr(LOG_DEBUG, szFunc, + "Received data when in wrong state on channel %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + /* Get call back function address + */ + if (_MDC_GetUserCB(nChanId, &pUserCB) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_GetUserCB failed for channel ID %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + if (pUserCB == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "User call back is NULL for channel ID %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + /* Call User call back. + Remove packet type character + */ + (*pUserCB) (nChanId, (szDeComData + 1), (nDataLen - 1)); + break; + + case MDC_ACK: + if (eChanSt != IN_SEND_REQUEST) + { + Lgr(LOG_DEBUG, szFunc, + "Received ACK when in wrong state on channel %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + if (_MDC_SetSRResult(nChanId, TRUE) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_SetSRResult failed for channel %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + if (_MDC_SetChState(nChanId, SEND_REQUEST_COMPLETE) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_SetChState failed for channel %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + break; + + case MDC_NAK: + if (eChanSt != IN_SEND_REQUEST) + { + Lgr(LOG_DEBUG, szFunc, + "Received ACK when in wrong state on channel %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + /* Extract and save Error String + */ + if (_MDC_GetNAKErrStr(nChanId, &pszNAKErrStr) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_GetNAKErrStr failed for channel %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + strcpy((char *) pszNAKErrStr, (char *) (szDeComData + 1)); + + if (_MDC_SetSRResult(nChanId, FALSE) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_SetSRResult failed for channel %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + if (_MDC_SetChState(nChanId, SEND_REQUEST_COMPLETE) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_SetChState failed for channel %d", nChanId); + if (szDeComData != szData) + free(szDeComData); + return; + } + + break; + + default: + /* Unrecognised message type */ + Lgr(LOG_DEBUG, szFunc, + "Unrecognised message type %c for channel ID %d", + eChanSt, nChanId); + break; + } + + if (szDeComData != szData) + free(szDeComData); +} + + +/****************************************************************************** + * Function: _MDC_CtrlCB + * Description: This function is called back when control information is received + * on any of the service connections + * Returns: void + ******************************************************************************/ +void _MDC_CtrlCB(int nType, ...) +{ + /* Local variables. + */ + UINT nChanId; + UINT nPortNo; + ULNG lIPaddr; + va_list pArgs; + UCHAR *szFunc = "_MDC_CtrlCB"; + + /* 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: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + /* Log link going down event for bug catching. + */ + Lgr(LOG_DEBUG, szFunc, + "New Service with client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + break; + + /* A connection has been made + */ + case SLC_CONNECT: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + if (nChanId == MDC.nPendConChanId) + { + MDC.nPendConChanId = 0; + MDC.nLinkUp = TRUE; + Lgr(LOG_DEBUG, szFunc, + "Connected to client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + } else + { + Lgr(LOG_DEBUG, szFunc, + "Rcvd unexpected connection: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + } + break; + + /* Given connection has become temporarily unavailable. + */ + case SLC_LINKDOWN: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + /* Log link going down event for bug catching. + */ + Lgr(LOG_DEBUG, szFunc, + "Link down to client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + + /* Reset flag to indicate that we dont have a connection. + */ + MDC.nLinkUp = FALSE; + break; + + /* Given connection has died. + */ + case SLC_LINKFAIL: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + /* Clean up for Channel ID + */ + if (SL_DelClient(nChanId) != R_OK) + { + Lgr(LOG_DEBUG, szFunc, + "SL_DelClient failed for Channel ID %d", nChanId); + } + + /* Log link failure event for bug catching. + */ + Lgr(LOG_DEBUG, szFunc, + "Link closed to client: nChanId=%d, nPortNo=%d, IP=%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); +} + +/****************************************************************************** + * Function: _MDC_SendPacket + * Description: Send a packet to a daemon + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_SendPacket(UINT nChanId, char cPacketType, UCHAR *psnzBuf, UINT nBuflen) +{ + char *psnzPktMsgBuf; /* buffer for preparing message to be sent */ + char *psnzComBuf; /* pointer to buffer containing compressed message */ + UCHAR *szFunc = "_MDC_SendPacket"; + UINT nlocalBufLen; /* Local buffer length variable */ + + nlocalBufLen = nBuflen + 1; + + psnzPktMsgBuf = malloc(nlocalBufLen); + if (psnzPktMsgBuf == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Cannot malloc"); + return(MDC_FAIL); + } + + /* Prepend packet type to service request data */ + *psnzPktMsgBuf = cPacketType; + memcpy((psnzPktMsgBuf + 1), psnzBuf, (nlocalBufLen - 1)); + + /* Log message that we are sending to ease debugging. + */ + Lgr(LOG_DEBUG, szFunc, "Sending packet (Before Compress): Data=%s, Len=%d", + psnzPktMsgBuf, nlocalBufLen); + + /* Compress packet to be sent + */ + psnzComBuf = Compress(psnzPktMsgBuf, &nlocalBufLen); + if (psnzComBuf == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Cannot malloc for Compress"); + free(psnzPktMsgBuf); + return(MDC_FAIL); + } + + /* Log message that we are sending to ease debugging. + */ + Lgr(LOG_DEBUG, szFunc, + "Sending packet (After Compress): nChanId=%d, Data=%s, Len=%d", + nChanId, psnzPktMsgBuf, nlocalBufLen); + + /* Send packet to the daemon */ + if (SL_SendData(nChanId, (UCHAR *) psnzComBuf, nlocalBufLen) != R_OK) + { + Lgr(LOG_DEBUG, szFunc, "SL_SendData failed"); + free(psnzPktMsgBuf); + if (psnzPktMsgBuf != psnzComBuf) + free(psnzComBuf); + return(MDC_FAIL); + } + + free(psnzPktMsgBuf); + if (psnzPktMsgBuf != psnzComBuf) + free(psnzComBuf); + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_GetSrvRqtReply + * Description: Get reply to a service request packet + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_GetSrvRqtReply(UINT nChanId, char *pcPacketType) +{ + /* Local variables. + */ + int nTotalTime; + UCHAR *szFunc = "_MDC_GetSrvRqtReply"; + + MDC.nPendSRChanId = nChanId; + + /* Wait for the service request reply + */ + nTotalTime = 0; + while (MDC.nSrvReqTimeout > nTotalTime && MDC.nPendSRChanId != 0) + { + SL_Poll((ULNG) SR_SLEEP_TIME); + nTotalTime += SR_SLEEP_TIME; + } + + if (MDC.nPendSRChanId != 0) + { + MDC.nPendSRChanId = 0; + Lgr(LOG_DEBUG, szFunc, "Timed out waiting for service request reply"); + return(MDC_FAIL); + } + + *pcPacketType = MDC.cReplyType; + + return(MDC_OK); +} + +/****************************************************************************** + * Function: _MDC_CreateChStatus + * Description: Make a new Channel status structure and add to linked list + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_CreateChStatus(UINT nChanId) /* Channel ID */ +{ + UINT nLocalChanId = nChanId; + UCHAR *szFunc = "_MDC_CreateChStatus"; + CHANSTATUS *psNewChanSt; + + /* Check that there is not already an entry for the Channel ID + */ + if (FindItem(MDC.spChanDetHead, &nLocalChanId, NULL, NULL) != NULL) + { + Lgr(LOG_DEBUG, szFunc, "Channel ID %d already in use", nLocalChanId); + return(MDC_FAIL); + } + + psNewChanSt = (CHANSTATUS *) malloc(sizeof(CHANSTATUS)); + if (psNewChanSt == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Cannot malloc"); + return(MDC_FAIL); + } + + psNewChanSt->nChanId = nLocalChanId; + psNewChanSt->State = MAKING_CONN; + psNewChanSt->UserDataCB = NULL; + + if (AddItem(&MDC.spChanDetHead, &MDC.spChanDetTail, SORT_NONE, &nLocalChanId, + NULL, NULL, psNewChanSt) != R_OK) + { + Lgr(LOG_DEBUG, szFunc, "AddItem failed for Channel ID %d", + nLocalChanId); + return(MDC_FAIL); + } + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_DelChStatus + * Description: Delete an item from the Channel status linked list + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_DelChStatus(UINT nChanId) /* Channel ID */ +{ + UINT nLocalChanId = nChanId; + UCHAR *szFunc = "_MDC_DelChStatus"; + CHANSTATUS *ChanSt; + + if ((ChanSt = FindItem(MDC.spChanDetHead, &nLocalChanId, NULL, NULL)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "FindItem failed: Channel ID %d not found", nLocalChanId); + return(MDC_FAIL); + } + + if (DelItem(&MDC.spChanDetHead, &MDC.spChanDetTail, NULL, &nLocalChanId, NULL, NULL) != R_OK) + { + Lgr(LOG_DEBUG, szFunc, + "DelItem failed: Channel ID %d not found", nLocalChanId); + return(MDC_FAIL); + } + + /* pds: 12/7/96. Ian forgot to tell the UX library that the channel had closed, so + * the following statement sends a request to UX to close the channel. + */ + SL_DelClient(nChanId); + free(ChanSt); + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_SetChState + * Description: Set Channel State to the given value + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_SetChState( UINT nChanId, /* Channel ID */ + CHSTATE eNewState) /* New state */ +{ + UINT nLocalChanId = nChanId; + UCHAR *szFunc = "_MDC_SetChState"; + CHANSTATUS *ChanSt; + + if ((ChanSt = FindItem(MDC.spChanDetHead, &nLocalChanId, NULL, NULL)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "FindItem failed: Channel ID %d not found", nLocalChanId); + return(MDC_FAIL); + } + + ChanSt->State = eNewState; + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_SetSRResult + * Description: Set Channel Send Request result + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_SetSRResult( UINT nChanId, /* Channel ID */ + UINT bResult) /* Send Request result */ +{ + UINT nLocalChanId = nChanId; + UCHAR *szFunc = "_MDC_SetSRResult"; + CHANSTATUS *ChanSt; + + if ((ChanSt = FindItem(MDC.spChanDetHead, &nLocalChanId, NULL, NULL)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "FindItem failed: Channel ID %d not found", nLocalChanId); + return(MDC_FAIL); + } + + ChanSt->bSendReqResult = bResult; + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_GetSRResult + * Description: Get Channel Send Request result + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_GetSRResult( UINT nChanId, /* Channel ID */ + UINT *bResult) /* Send Request result */ +{ + UINT nLocalChanId = nChanId; + UCHAR *szFunc = "_MDC_GetSRResult"; + CHANSTATUS *ChanSt; + + if ((ChanSt = FindItem(MDC.spChanDetHead, &nLocalChanId, NULL, NULL)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "FindItem failed: Channel ID %d not found", nLocalChanId); + return(MDC_FAIL); + } + + *bResult = ChanSt->bSendReqResult; + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_SetUserCB + * Description: Set Send Request call back to the given value + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_SetUserCB( UINT nChanId, /* Channel ID */ + void (*UserCB) (UINT, UCHAR *, UINT)) /* call back */ +{ + UINT nLocalChanId = nChanId; + UCHAR *szFunc = "_MDC_SetUserCB"; + CHANSTATUS *ChanSt; + + if ((ChanSt = FindItem(MDC.spChanDetHead, &nLocalChanId, NULL, NULL)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "FindItem failed: Channel ID %d not found", nLocalChanId); + return(MDC_FAIL); + } + + ChanSt->UserDataCB = UserCB; + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_GetChState + * Description: Get Channel State for a given channel + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_GetChState( UINT nChanId, /* Channel ID */ + CHSTATE *eState) /* Channel state */ +{ + UINT nLocalChanId = nChanId; + UCHAR *szFunc = "_MDC_GetChState"; + CHANSTATUS *ChanSt; + + if ((ChanSt = FindItem(MDC.spChanDetHead, &nLocalChanId, NULL, NULL)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "FindItem failed: Channel ID %d not found", nLocalChanId); + return(MDC_FAIL); + } + + *eState = ChanSt->State; + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_GetNAKErrStr + * Description: Get pointer to NAK error string + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_GetNAKErrStr( UINT nChanId, /* Channel ID */ + UCHAR **ppszErrStr) /* pointer to pointer to error string */ +{ + UINT nLocalChanId = nChanId; + UCHAR *szFunc = "_MDC_GetNAKErrStr"; + CHANSTATUS *ChanSt; + + if ((ChanSt = FindItem(MDC.spChanDetHead, &nLocalChanId, NULL, NULL)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "FindItem failed: Channel ID %d not found", nLocalChanId); + return(MDC_FAIL); + } + + *ppszErrStr = ChanSt->pszErrStr; + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_GetUserCB + * Description: Get User call back for the channel + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_GetUserCB( UINT nChanId, /* Channel ID */ + void (**UserCB) (UINT, UCHAR *, UINT)) /* User call back */ +{ + UINT nLocalChanId = nChanId; + UCHAR *szFunc = "_MDC_GetUserCB"; + CHANSTATUS *ChanSt; + + if ((ChanSt = FindItem(MDC.spChanDetHead, &nLocalChanId, NULL, NULL)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "FindItem failed: Channel ID %d not found", nLocalChanId); + return(MDC_FAIL); + } + + *UserCB = ChanSt->UserDataCB; + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_PrintErrMsg + * Description: Print error message text + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_PrintErrMsg( UCHAR *psnzErrMsg, /* Error message none terminated */ + UINT nBufLen) /* Buffer Length */ +{ + char *pszTmpBuf; + UCHAR *szFunc = "_MDC_PrintErrMsg"; + + pszTmpBuf = (char *) malloc(nBufLen + 1); + if (pszTmpBuf == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Cannot malloc"); + return(MDC_FAIL); + } + + memcpy(pszTmpBuf, psnzErrMsg, nBufLen); + + pszTmpBuf[nBufLen] = '\0'; + + Lgr(LOG_DEBUG, "", "Error Message: %s", pszTmpBuf); + + free(pszTmpBuf); + + return(MDC_OK); +} + + +/****************************************************************************** + * Function: _MDC_WaitOnSndReq + * Description: Block until send request completes + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int _MDC_WaitOnSndReq(UINT nChanId) /* Channel ID */ +{ + /* Local variables. + */ + UCHAR *szFunc = "_MDC_WaitOnSndReq"; + int nTotalTime; + int bSendreqCom; /* Indicates if the send request is complete */ + CHSTATE eChanSt; + + /* Wait for send request to complete + */ + nTotalTime = 0; + bSendreqCom = FALSE; + while (MDC.nSndReqTimeout > nTotalTime && bSendreqCom == FALSE) + { + SL_Poll((ULNG) SNDREQ_SLEEP_TIME); + if (_MDC_GetChState(nChanId, &eChanSt) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "No status information for channel ID %d", nChanId); + return(MDC_FAIL); + } + if(MDC.nLinkUp == FALSE) + { + Lgr(LOG_DEBUG, szFunc, "Link to daemon failed, aborting..."); + return(MDC_FAIL); + } + + if (eChanSt == SEND_REQUEST_COMPLETE) + bSendreqCom = TRUE; + + nTotalTime += SNDREQ_SLEEP_TIME; + } + + return(MDC_OK); +} + +/****************************************************************************** + * Function: MDC_SendRequest + * Description: Send a request to a driver + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int MDC_SendRequest( UINT nChanId, /* I: Channel to send message on */ + UCHAR *szData, /* I: Data to send */ + UINT nDataLen, /* I: Length of data */ + void (*DataCB) (UINT, UCHAR *, UINT)) + /* I: call back function for data */ +{ + /* Local variables. + */ + UCHAR *szFunc = "MDC_SendRequest"; + CHSTATE eCurrState; /* Current State for Channel ID */ + + /* If threading is enabled, then lock this function so that no other + * thread can enter. + */ + SINGLE_THREAD_LOCK + + /* Check if in MDC Comms mode + */ + if (MDC.nMDCCommsMode == FALSE) + { + Lgr(LOG_DEBUG, szFunc, "Not in MDC Comms Mode"); + THREAD_UNLOCK_RETURN(MDC_BADCONTEXT); + } + + if (_MDC_GetChState(nChanId, &eCurrState) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "Cannot get status for channel %d", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (eCurrState != IDLE) + { + Lgr(LOG_DEBUG, szFunc, "Channel state not IDLE"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Store user call back function + */ + if (_MDC_SetUserCB(nChanId, DataCB) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_SetUserCB failed for Channel ID %d", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (_MDC_SetChState(nChanId, IN_SEND_REQUEST) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "Cannot set channel status to IDLE"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (_MDC_SendPacket(nChanId, MDC_PREQ, szData, nDataLen) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_MDC_SendPacket failed"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Return success to caller as getting this far without hitch is success! + */ + THREAD_UNLOCK_RETURN(MDC_OK); +} + +/****************************************************************************** + * Function: MDC_GetResult + * Description: Wait for all replies to a send request and then return result + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int MDC_GetResult( UINT nChanId, /* I: Channel ID */ + UCHAR **ppszErrorMsg) /* O: Associated error message */ +{ + /* Local variables. + */ + UCHAR *szFunc = "MDC_GetResult"; + CHSTATE eChanSt; + UINT bReqResult; + + /* If threading is enabled, then lock this function so that no other + * thread can enter. + */ + SINGLE_THREAD_LOCK + + /* Check if in MDC Comms mode + */ + if (MDC.nMDCCommsMode == FALSE) + { + Lgr(LOG_DEBUG, szFunc, "Not in MDC Comms Mode"); + THREAD_UNLOCK_RETURN(MDC_BADCONTEXT); + } + + if (_MDC_GetChState(nChanId, &eChanSt) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "No status information for channel ID %d", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + switch(eChanSt) + { + case SEND_REQUEST_COMPLETE: + break; + + case IN_SEND_REQUEST: + /* Block until to Send Request Completes + */ + if(_MDC_WaitOnSndReq(nChanId) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_WaitOnSndReq failed for channel ID %d", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + break; + + default: + Lgr(LOG_DEBUG, szFunc, + "MDC_GetResult called when in state %c on channel %d", + eChanSt, nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + break; + } + + if (_MDC_SetChState(nChanId, IDLE) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "Cannot set channel state to IDLE for channel ID %d", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (_MDC_GetSRResult(nChanId, &bReqResult) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_GetSRResult failed for channel ID %d", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (_MDC_GetNAKErrStr(nChanId, ppszErrorMsg) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "_MDC_GetNAKErrStr failed for channel %d", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (bReqResult == FALSE) + THREAD_UNLOCK_RETURN(MDC_SNDREQNAK); + + /* Return success to caller as getting this far without hitch is success! + */ + THREAD_UNLOCK_RETURN(MDC_OK); +} + +/****************************************************************************** + * Function: MDC_GetStatus + * Description: Returns a boolean that indicates whether the Send Request for + * given channel has completed. + * Returns: MDC_OK or MDC_FAIL + ******************************************************************************/ +int MDC_GetStatus( UINT nChanId, /* I: Channel ID */ + UINT *bSndReqCom ) /* O: Indicates whether Send */ + /* Request has completed */ +{ + UCHAR *szFunc = "MDC_GetStatus"; + CHSTATE eChanSt; + + /* If threading is enabled, then lock this function so that no other + * thread can enter. + */ + SINGLE_THREAD_LOCK + + /* Check if in MDC Comms mode + */ + if (MDC.nMDCCommsMode == FALSE) + { + Lgr(LOG_DEBUG, szFunc, "Not in MDC Comms Mode"); + THREAD_UNLOCK_RETURN(MDC_BADCONTEXT); + } + + /* Call SL_Poll to process any outstanding messages + */ + SL_Poll(0); + + if (_MDC_GetChState(nChanId, &eChanSt) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "No status information for channel ID %d", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + switch(eChanSt) + { + case SEND_REQUEST_COMPLETE: + *bSndReqCom = TRUE; + break; + + case IN_SEND_REQUEST: + *bSndReqCom = FALSE; + break; + + default: + Lgr(LOG_DEBUG, szFunc, + "MDC_GetStatus called when in state %c on channel %d", + eChanSt, nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + break; + } + + /* Return success to caller as getting this far without hitch is success! + */ + THREAD_UNLOCK_RETURN(MDC_OK); +} + +/****************************************************************************** + * Function: MDC_CreateService + * Description: Create a connection to a daemon so that service requests can be + * issued + * Returns: Channel ID, or negative error code + ******************************************************************************/ +int MDC_CreateService( UCHAR *szHostName, /* I: Host for connect*/ + UINT *nPortNo, /* I: Port host on */ + SERVICEDETAILS *serviceDet ) /* I: Service details */ +{ + /* Statics. + */ + static int nProcessingFlag = FALSE; + + /* Local variables. + */ + ULNG lIPAddr; + UINT nServicesPortNo; + UCHAR *szFunc = "MDC_CreateService"; + int nTotalTime; /* total time waiting for connection to be made */ + int ChanId; + char ReplyPktType; + + /* If threading is enabled, then lock this function so that no other + * thread can enter. + */ + SINGLE_THREAD_LOCK + + /* Check to see that we are not in the midst of a create service. If we are, + * get out! + */ + if( nProcessingFlag == TRUE ) + { + Lgr(LOG_DEBUG, szFunc, "Not allowed to issue a %s from within a %s", + szFunc, szFunc); + THREAD_UNLOCK_RETURN(MDC_BADCONTEXT); + } + + /* Check if in MDC comms mode + */ + if (MDC.nMDCCommsMode != TRUE) + { + Lgr(LOG_DEBUG, szFunc, "Not in MDC Comms Mode"); + THREAD_UNLOCK_RETURN(MDC_BADCONTEXT); + } + + if (MDC.nPendCon == TRUE) + { + Lgr(LOG_DEBUG, szFunc, "Already an outstanding service request"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (SL_GetIPaddr(szHostName, &lIPAddr) != R_OK) + { + Lgr(LOG_DEBUG, szFunc, "SL_GetIPaddr failed"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* OK, we are active, so set the processing flag to avoid being clobbered. + */ + nProcessingFlag = TRUE; + + /* If the port number has not been given, then look one up. + */ + if (nPortNo == NULL) + { + if (SL_GetService(DEF_SERVICENAME, &nServicesPortNo) != R_OK) + { + Lgr(LOG_DEBUG, szFunc, "SL_GetService failed"); + nProcessingFlag = FALSE; + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + } + else + nServicesPortNo = *nPortNo; + + if ((ChanId = SL_AddClient(nServicesPortNo, lIPAddr, szHostName, + _MDC_DataCB, _MDC_CtrlCB)) < 0) + { + Lgr(LOG_DEBUG, szFunc, "SL_AddClient failed"); + nProcessingFlag = FALSE; + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Create Status structure for the new Channel + */ + if (_MDC_CreateChStatus(ChanId) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_MDC_CreateChStatus failed"); + nProcessingFlag = FALSE; + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + MDC.nPendConChanId = (UINT) ChanId; + + MDC.nPendCon = TRUE; + + /* Wait for an indication that the connection has been made + */ + nTotalTime = 0; + while (MDC.nNewSrvTimeout > nTotalTime && MDC.nPendConChanId != 0) + { + SL_Poll((ULNG) CS_SLEEP_TIME); + nTotalTime += CS_SLEEP_TIME; + } + + MDC.nPendCon = FALSE; + + if (MDC.nPendConChanId != 0) + { + /* No connection made + */ + Lgr(LOG_DEBUG, szFunc, + "Timed out making connection to daemon at %s, port no %d", + szHostName, nServicesPortNo); + MDC.nPendConChanId = 0; + SL_DelClient((UINT) ChanId); + _MDC_DelChStatus((UINT) ChanId); + nProcessingFlag = FALSE; + THREAD_UNLOCK_RETURN(MDC_NODAEMON); + } + + if (_MDC_SetChState((UINT) ChanId, IN_SERVICE_REQUEST) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_MDC_SendData failed"); + SL_DelClient((UINT) ChanId); + _MDC_DelChStatus((UINT) ChanId); + nProcessingFlag = FALSE; + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Send service request structure to the daemon + */ + if (_MDC_SendPacket((UINT) ChanId, MDC_INIT, (UCHAR *) serviceDet, + (UINT) sizeof(SERVICEDETAILS)) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_MDC_SendData failed"); + SL_DelClient((UINT) ChanId); + _MDC_DelChStatus((UINT) ChanId); + nProcessingFlag = FALSE; + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Get reply to service request. + */ + if (_MDC_GetSrvRqtReply((UINT) ChanId, &ReplyPktType) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_MDC_GetSrvRqtReply failed"); + SL_DelClient((UINT) ChanId); + _MDC_DelChStatus((UINT) ChanId); + nProcessingFlag = FALSE; + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (ReplyPktType == MDC_NAK) + { + Lgr(LOG_DEBUG, szFunc, "NAK reply received for service request"); + SL_DelClient((UINT) ChanId); + _MDC_DelChStatus((UINT) ChanId); + nProcessingFlag = FALSE; + THREAD_UNLOCK_RETURN(MDC_SERVICENAK); + } + + if (_MDC_SetChState((UINT) ChanId, IDLE) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "Cannot set channel status to IDLE"); + SL_DelClient((UINT) ChanId); + _MDC_DelChStatus((UINT) ChanId); + nProcessingFlag = FALSE; + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* OK, finished processing successfully, so reset flag. + */ + nProcessingFlag = FALSE; + + /* Get out, returning channel identifier of newly created service. + */ + THREAD_UNLOCK_RETURN(ChanId); +} + +/****************************************************************************** + * Function: MDC_SetTimeout + * Description: Function to program one of the system timeout values from the + * default to a user setting. + * Returns: MDC_OK - Setting changed. + * MDC_FAIL - Setting couldnt be changed, see error message. + ******************************************************************************/ +int MDC_SetTimeout( UCHAR *pszWhichTimeout, /* I: Timeout to set */ + UINT nTimeoutValue, /* I: New value */ + UCHAR *pszErrMsg ) /* O: Error message */ +{ + /* Local variables. + */ + UINT nReturn = MDC_OK; + UCHAR *szFunc = "MDC_SetTimeout"; + + /* If threading is enabled, then lock this function so that no other + * thread can enter. + */ + SINGLE_THREAD_LOCK + + /* Simply compare the string given by the caller and match against known + * strings. If we get a match, then set the timeout value to that given. + */ + if(strcmp(pszWhichTimeout, NEW_SERVICE_TIMEOUT) == 0) + { + MDC.nNewSrvTimeout = nTimeoutValue; + } else + if(strcmp(pszWhichTimeout, SRV_REQ_TIMEOUT) == 0) + { + MDC.nSrvReqTimeout = nTimeoutValue; + } else + if(strcmp(pszWhichTimeout, SEND_REQ_TIMEOUT) == 0) + { + MDC.nSndReqTimeout = nTimeoutValue; + } else + { + nReturn = MDC_FAIL; + } + + /* Exit with result code. + */ + THREAD_UNLOCK_RETURN(nReturn); +} + +/****************************************************************************** + * Function: MDC_CloseService + * Description: Close a service channel + * Returns: MDC_FAIL or MDC_OK or MDC_BADCONTEXT + ******************************************************************************/ +int MDC_CloseService( UINT nChanId ) /* I: Channel to close */ +{ + /* Local variables. + */ + UCHAR szTmpBuf[2]; + UCHAR *szFunc = "MDC_CloseService"; + + /* If threading is enabled, then lock this function so that no other + * thread can enter. + */ + SINGLE_THREAD_LOCK + + /* Prepare initial messages. + */ + sprintf(szTmpBuf, "%c", MDC_EXIT); + + /* Check if in MDC Comms mode + */ + if (MDC.nMDCCommsMode == FALSE) + { + Lgr(LOG_DEBUG, szFunc, "Not in MDC Comms Mode"); + THREAD_UNLOCK_RETURN(MDC_BADCONTEXT); + } + + /* Send command to remote to initiate closedown procedures. + */ + if (SL_BlockSendData(nChanId, (UCHAR *) szTmpBuf, strlen(szTmpBuf) != R_OK)) + { + Lgr(LOG_DEBUG, szFunc, + "Transmission of closedown message to client failed"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Give the UX library a slice of CPU so that it can tidy up. + SL_Poll(MAX_TERMINATE_TIME); + */ + + /* Delete the channel. + */ + if (_MDC_DelChStatus(nChanId) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_MDC_DelChStatus failed for Channel ID %d", + nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* All done, return completion code to caller. + */ + THREAD_UNLOCK_RETURN(MDC_OK); +} + +/****************************************************************************** + * Function: MDC_Start + * Description: This is called to initialise the MDC Comms. + * Returns: MDC_FAIL or MDC_OK + ******************************************************************************/ +int MDC_Start( void ) +{ + /* Local variables. + */ + int ret; + UCHAR *szFunc = "MDC_Start"; + + /* If threading is enabled, then lock this function so that no other + * thread can enter. + */ + SINGLE_THREAD_LOCK + + /* Setup logger mode. + */ + Lgr(LOG_CONFIG, LGM_STDOUT, LOG_MESSAGE, ""); + + if (_MDC_Init() != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_MDC_Init failed"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Check if MDC Comms is already initialised + */ + if (MDC.nMDCCommsMode == TRUE) + { + Lgr(LOG_DEBUG, szFunc, "Already in MDC Comms Mode"); + THREAD_UNLOCK_RETURN(MDC_BADCONTEXT); + } + + /* Initialise UX comms + */ + if ((ret = SL_Init(MDC_CL_KEEPALIVE, (UCHAR *) NULL)) != R_OK) + { + Lgr(LOG_DEBUG, szFunc, "SL_Init failed"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Now in MDC Comms Mode + */ + MDC.nMDCCommsMode = TRUE; + + /* Return success to caller as getting this far without hitch is success! + */ + THREAD_UNLOCK_RETURN(MDC_OK); +} + +/****************************************************************************** + * Function: MDC_End + * Description: This is called to shutdown the MDC Comms. + * Returns: MDC_FAIL or MDC_OK + ******************************************************************************/ +int MDC_End( void ) +{ + /* Local variables. + */ + int ret; + CHANSTATUS *spChanDet; + LINKLIST *spNext; + UCHAR *szFunc = "MDC_End"; + + /* If threading is enabled, then lock this function so that no other + * thread can enter. + */ + SINGLE_THREAD_LOCK + + /* Check if in MDC Comms mode + */ + if (MDC.nMDCCommsMode == FALSE) + { + Lgr(LOG_DEBUG, szFunc, "Not in MDC Comms Mode"); + THREAD_UNLOCK_RETURN(MDC_BADCONTEXT); + } + + /* Go through all current connections and close them if they are open. + */ + for(spChanDet=(CHANSTATUS *)StartItem(MDC.spChanDetHead, &spNext); + spChanDet != NULL; + spChanDet=(CHANSTATUS *)NextItem(&spNext)) + { + /* Force closure of connection. + */ + MDC_CloseService(spChanDet->nChanId); + } + + /* Final MDC termination tidy up. + */ + if (_MDC_Terminate() != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_SL_Terminate failed"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Shutdown UX comms + */ + if ((ret = SL_Exit((UCHAR *) NULL)) != R_OK) + { + Lgr(LOG_DEBUG, szFunc, "SL_Exit failed"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Exit MDC Comms Mode + */ + MDC.nMDCCommsMode = FALSE; + + /* Return success to caller as weve got to the end without hitch. + */ + THREAD_UNLOCK_RETURN(MDC_OK); +} + +/****************************************************************************** + * Function: MDC_ChangeService + * Description: Change Service for an existing daemon connection + * Returns: MDC_OK, MDC_FAIL, MDC_NODAEMON, MDC_NOSERVICE, MDC_BADPARMS + ******************************************************************************/ +int MDC_ChangeService( UINT nChanId, /* I: Chan ID of srvc */ + SERVICEDETAILS *serviceDet ) /* I: Service details */ +{ + /* Local variables. + */ + UCHAR *szFunc = "MDC_ChangeService"; + char ReplyPktType; + CHSTATE eChanSt; + + /* If threading is enabled, then lock this function so that no other + * thread can enter. + */ + SINGLE_THREAD_LOCK + + /* Check if in MDC comms mode + */ + if (MDC.nMDCCommsMode != TRUE) + { + Lgr(LOG_DEBUG, szFunc, "Not in MDC Comms Mode"); + THREAD_UNLOCK_RETURN(MDC_BADCONTEXT); + } + + /* Check that in IDLE state on channel + */ + if (_MDC_GetChState(nChanId, &eChanSt) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "No status information for channel ID %d", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (eChanSt != IDLE) + { + Lgr(LOG_DEBUG, szFunc, "Channel ID %d not in idle state", nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (_MDC_SetChState(nChanId, IN_CHANGE_SERVICE) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, + "Cannot set status to IN_CHANGE_SERVICE for channel ID %d",nChanId); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Send service request structure to the daemon. + */ + if (_MDC_SendPacket((UINT)nChanId, MDC_CHANGE, (UCHAR *) serviceDet, + (UINT) sizeof(SERVICEDETAILS)) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_MDC_SendPacket failed"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Get reply to service request. + */ + if (_MDC_GetSrvRqtReply((UINT)nChanId, &ReplyPktType) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "_MDC_GetSrvRqtReply failed"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + if (ReplyPktType == MDC_NAK) + { + Lgr(LOG_DEBUG, szFunc, "NAK reply received for service request"); + THREAD_UNLOCK_RETURN(MDC_SERVICENAK); + } + + if (_MDC_SetChState((UINT)nChanId, IDLE) != MDC_OK) + { + Lgr(LOG_DEBUG, szFunc, "Cannot set channel status to IDLE"); + THREAD_UNLOCK_RETURN(MDC_FAIL); + } + + /* Return new channel id, shouldnt really have changed. + */ + THREAD_UNLOCK_RETURN(nChanId); +} diff --git a/MDC/mdc_common.c b/MDC/mdc_common.c new file mode 100755 index 0000000..4bba917 --- /dev/null +++ b/MDC/mdc_common.c @@ -0,0 +1,148 @@ +/****************************************************************************** + * Product: # # ###### ##### # ### ###### + * ## ## # # # # # # # # + * # # # # # # # # # # # + * # # # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * # # ###### ##### ####### ####### ### ###### + * + * File: mdc_common.c + * Description: Meta Data Communications common functions shared between + * the client and server API interfaces. + * + * 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. +*/ +#if defined(SOLARIS) +#include +#endif +#include +#include +#include +#include +#include +#include + +/* Indicate that we are a C module for any header specifics. +*/ +#define MDC_COMMON_C + +/* Bring in local specific header files. +*/ +#include "mdc.h" +#include "mdc_common.h" + +/****************************************************************************** + * Function: _MDC_Init + * Description: Function to perform MDC initialisation. The library performs + * checking to ensure that initialisation only occurs once + * prior to an _MDC_Terminate. + * + * Returns: MDC_FAIL- Couldnt initialise library. + * MDC_OK - Library initialised. + ******************************************************************************/ +int _MDC_Init( void ) +{ + /* Local variables. + */ + static int nInitialised = FALSE; + int nReturn; + UCHAR *szFunc = "_MDC_Init"; + + /* If this is the first invocation, setup the global state flag to + * a known value. + */ + if(nInitialised == FALSE) + { + nInitialised = TRUE; + MDC.nInitialised = FALSE; + + MDC.nMDCCommsMode = FALSE; + MDC.spChanDetHead = NULL; + MDC.spChanDetTail = NULL; + } + + /* Have we been initialised already..? If we have, just get out. + */ + if( MDC.nInitialised == FALSE ) + { + /* Initialise all so-called globals. + */ + MDC.spIFifoHead = NULL; + MDC.spIFifoTail = NULL; + MDC.spOFifoHead = NULL; + MDC.spOFifoTail = NULL; + MDC.nClientChanId = 0; + MDC.nCloseDown = FALSE; + MDC.nInitialised = TRUE; + MDC.nNewSrvTimeout = DEF_NEW_SERVICE_TIMEOUT; + MDC.nSrvReqTimeout = DEF_SRV_REQ_TIMEOUT; + MDC.nSndReqTimeout = DEF_SEND_REQ_TIMEOUT; +#if defined(SOLARIS) + /* MDC.thMDCLock = DEFAULTMUTEX; */ +#endif + + /* Initialise UX comms + */ + if ((nReturn = SL_Init(MDC_SRV_KEEPALIVE, (UCHAR *) NULL)) != R_OK) + { + Lgr(LOG_DEBUG, szFunc, "SL_Init failed"); + return(MDC_FAIL); + } + } + + /* Return result to caller. + */ + return(MDC_OK); +} + +/****************************************************************************** + * Function: _MDC_Terminate + * Description: Function to shutdown the MDC library. After a successful + * shutdown, _MDC_Init may be called to re-initialise the + * library. If MDC_Terminate fails, program exit is advised. + * + * Returns: MDC_FAIL- Couldnt perform a clean shutdown. + * MDC_OK - Library successfully shutdown. + ******************************************************************************/ +int _MDC_Terminate( void ) +{ + /* Local variables. + */ + + /* Free up all resources that weve used to return us to a virgin state. + */ + + /* Give the UX library a slice of CPU so that it can tidy up. + */ + SL_Poll(MAX_TERMINATE_TIME); + + /* Reset the initialisation flag so that user code can re-initialise us. + */ + MDC.nInitialised = FALSE; + + /* Return result to caller. + */ + return(MDC_OK); +} diff --git a/MDC/mdc_common.h b/MDC/mdc_common.h new file mode 100755 index 0000000..1804a69 --- /dev/null +++ b/MDC/mdc_common.h @@ -0,0 +1,211 @@ +/****************************************************************************** + * Product: # # ###### ##### # ### ###### + * ## ## # # # # # # # # + * # # # # # # # # # # # + * # # # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * # # ###### ##### ####### ####### ### ###### + * + * File: mdc_common.h + * Description: General purpose library routines for MDC. + * + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef MDC_COMMON_H +#define MDC_COMMON_H + +/* Define constants, definitions etc. +*/ +#define DEF_POLLTIME 1000 /* Default comms poll wait time */ +#define DEF_SERVICENAME "vdwd" /* Name of service in /etc/services */ +#define MDC_SRV_KEEPALIVE 1000 /* TCP/IP keep alive for MDC Server */ +#define MAX_TERMINATE_TIME 2000 /* Time for termination of MDC layer */ + +/* Timeout definitions. +*/ +#define MDC_CL_KEEPALIVE 1000 /* TCP/IP keep alive for MDC client */ +#define CS_SLEEP_TIME 1 /* Create service loop sleep time (mS)*/ +#define SR_SLEEP_TIME 10 /* Srv req reply loop sleep time (mS) */ +#define SNDREQ_SLEEP_TIME 10 /* Wait on send req sleep time (mS) */ +#define DEF_NEW_SERVICE_TIMEOUT 30000 /* Number of mS before give up trying */ + /* to make the connection */ +#define DEF_SRV_REQ_TIMEOUT 10000 /* Number of mS to wait for Service */ + /* Request reply */ +#define DEF_SEND_REQ_TIMEOUT 5400000 /* Number of mS before timing out */ +#define NEW_SERVICE_TIMEOUT "NEW_SERVICE" +#define SRV_REQ_TIMEOUT "SERVICE_REQUEST" +#define SEND_REQ_TIMEOUT "SEND_REQUEST" + +/* Globals (yugghhh!) for the MDC library. They are contained within a + * structure so they are more manageable and readers can see immediately + * the variables scope. +*/ +typedef struct { + /* Shared Globals + */ + UCHAR szErrMsg[MAX_ERRMSGLEN]; /* Storage for error messages */ + + /* Server Globals + */ + LINKLIST *spIFifoHead; /* Pointer to head of Incoming FIFO list */ + LINKLIST *spIFifoTail; /* Pointer to tail of Incoming FIFO list */ + LINKLIST *spOFifoHead; /* Pointer to head of Outgoing FIFO list */ + LINKLIST *spOFifoTail; /* Pointer to tail of Outgoing FIfO list */ + UINT nClientChanId; /* Server to client comms channel Id */ + UINT nCloseDown; /* Shutdown flag */ + UINT nInitialised; /* Flag to indicate if library initialised */ + void (*fCntrlCB)( UCHAR ); /* User Control callback */ + + /* Client Globals + */ + UINT nMDCCommsMode; /* Indicates whether the Client software */ + /* is in MDC Comms Mode, i.e. between an */ + /* MDC_start and MDC_End */ + LINKLIST *spChanDetHead; /* Pointer to head of Channel Details list */ + LINKLIST *spChanDetTail; /* Pointer to tail of Channel Details list */ + UINT nNewSrvTimeout; /* New service timeout */ + UINT nSrvReqTimeout; /* Service Request timeout */ + UINT nSndReqTimeout; /* Send Request timeout */ + UINT nLinkUp; /* Indicates if the link is up and running */ + UINT nPendCon; /* Indicates whether a connection is */ + /* pending */ + UINT nPendConChanId; /* Indicates chanid for the pending */ + /* connection, control call back sets */ + /* this to zero when connection received */ + UINT nPendSRChanId; /* Indicates chanid for a pending */ + /* service request reply */ + /* 0: no pending service request reply */ + char cReplyType; /* Reply to service request ACK or NAK */ +#if defined(MDC_CLIENT_C) && defined(SOLARIS) + mutex_t thMDCLock; /* Single thread lock for MT environment */ +#endif +} MDC_GLOBALS; + +/* Declare any common module prototypes which can only be seen by the MDC + * API library. +*/ +#if defined(MDC_COMMON_C) || defined(MDC_CLIENT_C) || defined(MDC_SERVER_C) + int _MDC_Init( void ); + int _MDC_Terminate( void ); +#endif + +/* Declare any required data types or variables which are specific to the + * common module. +*/ +#ifdef MDC_COMMON_C + MDC_GLOBALS MDC; +#endif + +/* Declare any required data types or variables which are specific to the + * client API module. +*/ +#ifdef MDC_CLIENT_C + extern MDC_GLOBALS MDC; + + typedef enum ChState + { + MAKING_CONN, /* Making connection to daemon */ + IN_SERVICE_REQUEST, /* Waiting for reply to service request */ + IDLE, /* Waiting for a send request, Change */ + /* service or Close service */ + IN_CHANGE_SERVICE, /* In process of changing service */ + IN_SEND_REQUEST, /* Processing a request */ + SEND_REQUEST_COMPLETE, /* Processing for send request is complete. */ + /* Will go to IDLE after user has done a */ + /* GetStatus for the Channel */ + } CHSTATE; + + typedef struct ChanStatus + { + UINT nChanId; /* Channel ID for connection */ + CHSTATE State; /* State for channel */ + void (*UserDataCB) (UINT, UCHAR *, UINT); + /* Call back function for send request */ + + /* The following are only valid if the state is + SEND_REQUEST_COMPLETE + */ + UINT bSendReqResult; /* Result of send request */ + UCHAR pszErrStr[MAX_ERRMSGLEN]; + /* If NAK returned then contains */ + /* the NAK Error String */ + } CHANSTATUS; + + int _MDC_SendPacket(UINT, char, UCHAR *, UINT); + int _MDC_PrintErrMsg(UCHAR *, UINT); + int _MDC_GetSrvRqtReply(UINT, char *); + int _MDC_CreateChStatus(UINT); + int _MDC_DelChStatus(UINT); + int _MDC_SetChState(UINT, CHSTATE); + int _MDC_GetChState(UINT, CHSTATE *); + int _MDC_SetSRResult(UINT, UINT); + int _MDC_GetSRResult(UINT, UINT *); + int _MDC_GetNAKErrStr(UINT, UCHAR **); + int _MDC_SetUserCB(UINT, void (*) (UINT, UCHAR *, UINT)); + int _MDC_GetUserCB(UINT, void (**) (UINT, UCHAR *, UINT)); + int _MDC_WaitOnSndReq( UINT ); +#endif + +/* Declare any required data types or variables which are specific to the + * server API module. +*/ +#ifdef MDC_SERVER_C + /* Structure to hold incoming and outgoing data packets within a FIFO + * linklist mechanism. + */ + typedef struct { + UCHAR *pszData; /* Pointer to a data block */ + UINT nDataLen; /* Length of data contained in block */ + } FIFO; + + /* External global variable. + */ + extern MDC_GLOBALS MDC; + + /* Prototypes for functions internal to MDC Server module. + */ + int _MDC_SendACK( void ); + int _MDC_SendNAK( UCHAR * ); + void _MDC_ServerCntlCB( int, ... ); + void _MDC_ServerDataCB( UINT, UCHAR *, UINT ); +#endif + +/* A set of macros for implementing a simple thread locking strategy + * for the MDC library under solaris. The strategy is that only one + * thread may have access to the MDC libraries at any one time. Eventually + * the library will become completely thread-safe. +*/ +#if defined(SOLARIS) +#define SINGLE_THREAD_LOCK mutex_lock(&MDC.thMDCLock); +#define THREAD_UNLOCK_RETURN(p) { \ + mutex_unlock(&MDC.thMDCLock); \ + return(p); \ + } +#else +#define SINGLE_THREAD_LOCK +#define THREAD_UNLOCK_RETURN(p) return(p) +#endif + +#endif /* MDC_COMMON_H */ diff --git a/MDC/mdc_server.c b/MDC/mdc_server.c new file mode 100755 index 0000000..7f44733 --- /dev/null +++ b/MDC/mdc_server.c @@ -0,0 +1,815 @@ +/****************************************************************************** + * 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); +} diff --git a/SDD/Makefile b/SDD/Makefile new file mode 100755 index 0000000..990cfe2 --- /dev/null +++ b/SDD/Makefile @@ -0,0 +1,156 @@ +#****************************************************************************** +#* Product: ##### ###### ###### # ### ###### +#* # # # # # # # # # # +#* # # # # # # # # # +#* ##### # # # # # # ###### +#* # # # # # # # # # +#* # # # # # # # # # # +#* ##### ###### ###### ####### ####### ### ###### +#* +#* File: Makefile +#* Description: Build description file for the virtual data warehouse +#* daemon Server Data-source Drivers. +#* 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 . +#****************************************************************************** +TITLE = "Server Data-source Driver Library" +COPYRIGHT = "(C) P.D.Smart, %D%, Ver %I%" +PROJ = +PURIFY = #purify +PROJPATH = ../SDD +GNUINCLUDE = -I/apps/gnu/$(ARCH)/include +MDCINCLUDE = -I../MDC +UXINCLUDE = -I../ux +ODBCINCLUDE = -I../odbc/include +SYBINCLUDE = -I/apps/sybase/include +INCLUDEDIR = -I. $(UXINCLUDE) $(MDCINCLUDE) $(GNUINCLUDE) $(ODBCINCLUDE)\ + $(SYBINCLUDE) +1DEBUGFLAGS = -g #-DMDC_DEBUG #-E +4DEBUGFLAGS = -g #-DMDC_DEBUG #-E +5DEBUGFLAGS = -g #-DMDC_DEBUG #-E +1OPTIMIZEFLAGS = #-O2 +4OPTIMIZEFLAGS = #-O2 +5OPTIMIZEFLAGS = #-O2 +1OPTIONFLAGS = -D${OS} #-ansi -Wall +4OPTIONFLAGS = -D${OS} #-ansi -Wall +5OPTIONFLAGS = -D${OS} -D_REENTRANT #-ansi -Wall +CFLAGS = $(${OSVER}DEBUGFLAGS) $(${OSVER}OPTIMIZEFLAGS) \ + $(${OSVER}OPTIONFLAGS) +LDFLAGS = -static +ODBCLIBS = -I../odbc/dlls -lodbc +LIBS = -lm +SCCSFLAGS = -d$(PROJPATH) +SCCSGETFLAGS = + +ifeq ($(ZPU_BUILD),) +BASE = +else +BASE = zpu-elf- +endif + +CC = $(BASE)gcc +LD = $(BASE)gcc +AS = $(BASE)as +AR = $(BASE)ar +CP = $(BASE)objcopy +DUMP = $(BASE)objdump +RANLIB = $(BASE)ranlib + +# Suffixes where interested in for this project. +# +.SUFFIXES: +.SUFFIXES: .o .c .h + +# Our way of making an object file. +# +.c.o: + $(PURIFY) $(CC) $(INCLUDEDIR) $(CFLAGS) -c $< + +# All, ie: all programs to be built +# +all: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Use 'build' command to make LIBSDD library." + +libsdd: Begin \ + libsdd.a \ + End + +# How to clean up the directory... make it look pretty! +# +clean: Begin \ + DoClean \ + End + +# How to perform an installation of the resultant software. +# +install: Begin \ + DoInstall \ + End + +# +# Pre-make start sequence. +# +Begin: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Operation commencing @ `date`" + @echo + +# +# Post-make completion sequence. +# +End: + @echo + @echo "Completed @ `date`" + +# Perform all cleanup operations to ensure future builds occur +# with completeness. +# +DoClean: + rm -f *.o *.bak *.a *.BAK *.sav core + +# Perform installation of software as per spec. +# +DoInstall: + +# Build the virtual data warehouse project Server +# Data-source Driver library. +# +libsdd.a: sdd_java.o sdd_scmd.o sdd_ftpx.o sdd_sybc.o + $(AR) rcv libsdd.a sdd_java.o sdd_scmd.o sdd_ftpx.o \ + sdd_sybc.o +#libsdd.a: sdd_java.o sdd_scmd.o sdd_ftpx.o sdd_aupl.o +# $(AR) rcv libsdd.a sdd_java.o sdd_scmd.o sdd_ftpx.o sdd_aupl.o + +sdd_java.o: sdd.h sdd_java.h + +sdd_odbc.o: sdd.h sdd_odbc.h + +sdd_scmd.o: sdd.h sdd_scmd.h + +sdd_ftpx.o: sdd.h sdd_ftpx.h + +sdd_sybc.o: sdd.h sdd_sybc.h + +sdd_aupl.o: sdd.h sdd_aupl.h diff --git a/SDD/build b/SDD/build new file mode 100755 index 0000000..a8d499f --- /dev/null +++ b/SDD/build @@ -0,0 +1,174 @@ +#!/bin/csh +#****************************************************************************** +#* Product: ##### ###### ###### # ### ###### +#* # # # # # # # # # # +#* # # # # # # # # # +#* ##### # # # # # # ###### +#* # # # # # # # # # +#* # # # # # # # # # # +#* ##### ###### ###### ####### ####### ### ###### +#* +#* File: build +#* Description: Build file for making different versions of the Server +#* Data-source Driver library. +#* 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 . +#****************************************************************************** + +# Work out architecture we are compiling on.. +# +setenv ARCH `uname -s` +setenv ARCH ${ARCH}`uname -r | cut -c1` + +# Work out arguments and decide on actions from there. +# +if( $#argv > 0 ) then + + switch("$argv[1]") + + case "save": + make -f Makefile clean + sccs delta SCCS + exit 0 + + case "zpu": + setenv ARCH 'ZPU' + set makecmd="libsdd" + breaksw + + case "install": + echo "Nothing to install, drivers specific to VDWD." + exit 0 + + default: + # If the command is not one this script recognises then pass on to + # the makefile. + # + set makecmd="$argv[1]" + endsw +else + set makecmd="libsdd" +endif + +# Build according to parameter supplied. +# +switch ($ARCH) + case "SunOS4": + setenv OS "SUNOS" + setenv OSVER 4 + echo "SunOS operating system build" + + if( ! -r .sunos ) then + \rm -f .solaris + \rm -f .sunos + \rm -f .linux + \rm -f .zpu + touch .sunos + chmod 777 .sunos + make -f Makefile clean $makecmd + set result = $status + else + make -f Makefile $makecmd + set result = $status + endif + if( $result == 0 && -r libsdd.a ) then + \rm -f ${OSVER}lib/libsdd.a + ranlib ./libsdd.a + \mv -f libsdd.a ${OSVER}lib + endif + breaksw + + case "SunOS5": + setenv OS "SOLARIS" + setenv OSVER 5 + echo "Solaris operating system build" + + if( ! -r .solaris ) then + \rm -f .solaris + \rm -f .sunos + \rm -f .linux + \rm -f .zpu + touch .solaris + chmod 777 .solaris + make -f Makefile clean $makecmd + set result = $status + else + make -f Makefile $makecmd + set result = $status + endif + if( $result == 0 && -r libsdd.a ) then + \rm -f ${OSVER}lib/libsdd.a + \mv -f libsdd.a ${OSVER}lib + endif + breaksw + + case "Linux2": + case "Linux4": + setenv OS "LINUX" + setenv OSVER 1 + echo "Linux operating system build" + echo "" + if( ! -r .linux ) then + \rm -f .sunos + \rm -f .solaris + \rm -f .linux + \rm -f .zpu + touch .linux + chmod 777 .linux + make -f Makefile clean $makecmd + set result = $status + else + make -f Makefile $makecmd + set result = $status + endif + if( $result == 0 && -r libsdd.a ) then + \rm -f ${OSVER}lib/libsdd.a + \mv -f libsdd.a ${OSVER}lib + endif + breaksw + + case "ZPU": + setenv OS "ZPU" + setenv OSVER 1 + echo "ZPU operating system build" + echo "" + if( ! -r .zpu ) then + \rm -f .sunos + \rm -f .solaris + \rm -f .linux + \rm -f .zpu + touch .zpu + chmod 777 .zpu + make -f Makefile clean $makecmd + set result = $status + else + make -f Makefile ZPU_BUILD=1 $makecmd + set result = $status + endif + if( $result == 0 && -r libsdd.a ) then + \rm -f ${OSVER}lib/libsdd.a + \mv -f libsdd.a ${OSVER}lib + endif + breaksw + + default: + echo "Unknown architecture, cannot build library..." + breaksw +endsw diff --git a/SDD/sdd.h b/SDD/sdd.h new file mode 100755 index 0000000..a592b15 --- /dev/null +++ b/SDD/sdd.h @@ -0,0 +1,159 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd.h + * Description: Server Data-source Driver library driver global header file + * for all library drivers. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef SDD_H +#define SDD_H + +/* Bring in the MDC Library global definitions header file as we need to + * know about SERVICEDETAILS. +*/ +#include + +/* Define any default values common to all drivers. +*/ +#define DEF_COLSEP "|" +#define DEF_PADCHR " " + +/* Define return types. +*/ +#define SDD_OK 0 /* Success reture code */ +#define SDD_FAIL -1 /* Fail return code */ + +/* Define driver sub-commands. +*/ +#define SDD_ABORT 'A' /* Abort command */ +#define SDD_EXIT 'E' /* Exit command */ + +/* System command module commands. +*/ +#define SDD_CMD_DEL 'D' /* Perform a delete on D host */ +#define SDD_CMD_EXEC 'X' /* Execute a cmd on the local system */ +#define SDD_CMD_EXEC_TIMED 'T' /* Execute a cmd on local system @ Time */ +#define SDD_CMD_MOVE 'M' /* Perform a rename/move on D host */ +#define SDD_CMD_READ 'R' /* Perform a read operation D -> Client */ +#define SDD_CMD_WRITE 'W' /* Perform a write operation D <- Client */ + +/* FTP sub-commands. +*/ +#define SDD_FTPX_RCV 'R' /* FTP Receive a File command */ +#define SDD_FTPX_XMIT 'X' /* FTP Transmit a File command */ +#define SDD_FTPX_REN 'M' /* FTP Rename a File command */ + +/* Database (Sybase, ODBC) sub-commands). +*/ +#define SDD_LIST_DB 'F' /* List all database names on data source */ +#define SDD_LIST_COLS 'G' /* List all column names on data source */ +#define SDD_LIST_TABLES 'H' /* List all tables names on data source */ +#define SDD_RUNSQL 'R' /* Execute an SQL statement */ + +/* Audio sub-commands. +*/ +#define SDD_PLAY_Z 'Z' /* Play a compressed audio file */ + +/* Define driver data return sub-commands. +*/ +#define SDD_DATABLOCK 'D' /* Data is one block out of many blocks */ +#define SDD_ENDBLOCK 'E' /* Data is last block in series */ + +/* Define error return codes which are embedded into returned error messages + * for the user to decipher. +*/ +#define SDD_EMSG_MEMORY "D0001" +#define SDD_EMSG_SQLLOAD "D0002" +#define SDD_EMSG_SQLSYNTAX "D0003" +#define SDD_EMSG_SQLRUNERR "D0004" +#define SDD_EMSG_FETCHHEAD "D0005" +#define SDD_EMSG_FETCHROW "D0006" +#define SDD_EMSG_SENDROW "D0007" +#define SDD_EMSG_ABORTRCVD "D0008" +#define SDD_EMSG_SRCINIT "D0009" +#define SDD_EMSG_BADLOGIN "D0010" +#define SDD_EMSG_BADDBASE "D0011" +#define SDD_EMSG_BADREQ "D0012" +#define SDD_EMSG_BADCMD "D0013" +#define SDD_EMSG_BADPATH "D0014" +#define SDD_EMSG_BADARGS "D0015" +#define SDD_EMSG_BADTIME "D0016" +#define SDD_EMSG_BADEXEC "D0017" +#define SDD_EMSG_BADEXIT "D0018" +#define SDD_EMSG_BADFREE "D0019" +#define SDD_EMSG_NOTYI "D0020" +#define SDD_EMSG_BADBLOCKSZ "D0021" +#define SDD_EMSG_FILEERROR "D0022" +#define SDD_EMSG_BADFILE "D0023" + +/* JAVA Driver. +*/ +int java_InitService( SERVICEDETAILS *, UCHAR * ); +int java_CloseService( UCHAR * ); +int java_ProcessRequest( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +void java_ProcessOOB( UCHAR ); + +/* ODBC Driver. +*/ +int odbc_InitService( SERVICEDETAILS *, UCHAR * ); +int odbc_CloseService( UCHAR * ); +int odbc_ProcessRequest( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +void odbc_ProcessOOB( UCHAR ); + +/* System Command Driver. +*/ +int scmd_InitService( SERVICEDETAILS *, UCHAR * ); +int scmd_CloseService( UCHAR * ); +int scmd_ProcessRequest( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +void scmd_ProcessOOB( UCHAR ); + +/* FTP Driver. +*/ +int ftpx_InitService( SERVICEDETAILS *, UCHAR * ); +int ftpx_CloseService( UCHAR * ); +int ftpx_ProcessRequest( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +void ftpx_ProcessOOB( UCHAR ); + +/* SYBASE Driver. +*/ +int sybc_InitService( SERVICEDETAILS *, UCHAR * ); +int sybc_CloseService( UCHAR * ); +int sybc_ProcessRequest( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +void sybc_ProcessOOB( UCHAR ); + +/* Audio Player Driver. +*/ +int aupl_InitService( SERVICEDETAILS *, UCHAR * ); +int aupl_CloseService( UCHAR * ); +int aupl_ProcessRequest( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +void aupl_ProcessOOB( UCHAR ); + +#endif /* SDD_H */ diff --git a/SDD/sdd_aupl.c b/SDD/sdd_aupl.c new file mode 100755 index 0000000..2c3774b --- /dev/null +++ b/SDD/sdd_aupl.c @@ -0,0 +1,522 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_aupl.c + * Description: Server Data-source Driver library driver to handle playback + * of 16bit Stereo Audio WAV file to the DSP circuitry of + * an underlying sound card. + * + * Version: %I% + * Dated: %D% + * Copyright: P.D. Smart, 1997-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 + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define SDD_AUPL_C + +/* Bring in local specific header files. +*/ +#include "sdd.h" +#include "sdd_aupl.h" + +/****************************************************************************** + * Function: _AUPL_GetStrArg + * Description: Function to scan an input buffer and extract a string based + * argument. + * + * Returns: SDD_FAIL- Couldnt obtain argument. + * SDD_OK - Argument obtained. + ******************************************************************************/ +int _AUPL_GetStrArg( UCHAR *snzDataBuf, /* I: Input buffer */ + int nDataLen, /* I: Len of data */ + UCHAR *szArg, /* I: Arg to look for */ + UCHAR **pszPath ) /* O: Pointer to argument */ +{ + /* Local variables. + */ + int nCmpLen; + int nPos; + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_AUPL_GetStrArg"; + + /* Go through the input buffer looking for 'szArg' within the + * input buffer. If it exists, then note the position just after it + * as this contains the string argument. + */ + for(nPos=0, nCmpLen=strlen(szArg); nPos < nDataLen; nPos++) + { + /* If a match occurs, then setup the pointer to correct location. + */ + if(strncmp(&snzDataBuf[nPos], szArg, nCmpLen) == 0) + { + /* Make sure that the match is not a sub-match as some of the + * variables we are scanning for are similar. + */ + if( (nPos == 0) || + (nPos > 0 && snzDataBuf[nPos-1] == '\0') || + (nPos > 0 && isspace(snzDataBuf[nPos-1])) ) + { + nPos += nCmpLen; + break; + } + } + } + + /* If the pointer did not reach the end of the buffer then we have + * located a valid name. + */ + if(nPos < nDataLen) + { + /* Setup the callers pointer to point to the correct location + * in the buffer. + */ + *pszPath = &snzDataBuf[nPos]; + nReturn=SDD_OK; + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _AUPL_ValidatePath + * Description: Function to validate the existence of a path. + * + * Returns: SDD_FAIL- Couldnt validate PATH. + * SDD_OK - PATH validated. + ******************************************************************************/ +int _AUPL_ValidatePath( UCHAR *pszPath ) /* I: Path to validate */ +{ + /* Local variables. + */ + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_AUPL_ValidatePath"; + struct stat sStat; + + /* Test the path to ensure its valid. + */ + if( (stat(pszPath, &sStat) == -1) || + ((sStat.st_mode & S_IFDIR) == 0) ) + { + nReturn=SDD_FAIL; + } else + { + nReturn=SDD_OK; + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _AUPL_ValidateFile + * Description: Function to validate the existence of a file or to validate + * that a file can be created. + * + * Returns: SDD_FAIL- Couldnt obtain Filename or validate it. + * SDD_OK - Filename obtained and validated. + ******************************************************************************/ +int _AUPL_ValidateFile( UCHAR *pszPath, /* I: Path to file */ + UCHAR *pszFile, /* I: File to validate */ + UINT nWriteFlag ) /* I: Read = 0, Write = 1 */ +{ + /* Local variables. + */ + int nReturn = SDD_FAIL; + UCHAR szTmpBuf[MAX_TMPBUFLEN]; + UCHAR *szFunc = "_AUPL_ValidateFile"; + struct stat sStat; + FILE *fpTest; + + /* Concatenate the file and path together, ready to perform a test. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + sprintf(szTmpBuf, "%s//%s", pszPath, pszFile); +#endif +#if defined(_WIN32) + sprintf(szTmpBuf, "%s%c%s", pszPath, 0x5c, pszFile); +#endif + + /* Is the file to be created? + */ + if(nWriteFlag == TRUE) + { + /* Test the file by trying to create it, see if the underlying OS + * can perform this operation. + */ + if((fpTest=fopen(szTmpBuf, "w")) != NULL) + { + /* Close and remove the file we created, not needed yet. + */ + fclose(fpTest); + unlink(szTmpBuf); + nReturn = SDD_OK; + } else + { + nReturn = SDD_FAIL; + } + } else + { + /* Test the file to ensure that it exists and is readable. + */ + if( (stat(szTmpBuf, &sStat) == -1) || + ((sStat.st_mode & S_IFREG) == 0) ) + { + nReturn=SDD_FAIL; + } else + { + nReturn=SDD_OK; + } + } + + /* Return result code to caller. + */ + return(nReturn); +} + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) +/****************************************************************************** + * Function: _AUPL_PlayZ + * Description: Function to play a compressed audio file. Method of attach + * is to launch a child which is the actual decompressor, this + * feeds data back via the stdout of the child to our stdin. The + * data is then buffered in a round robin fashion and fed to the + * audio DSP hardware. + * + * Returns: SDD_FAIL- Command failed during execution. + * SDD_OK - Command executed successfully. + ******************************************************************************/ +int _AUPL_PlayZ( UCHAR *pszAudioPath, /* I: Path to Audio File */ + UCHAR *pszAudioFile, /* I: Audio Filename */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: Func for returning data */ + UCHAR *szErrMsg ) /* O: Error message generated */ +{ + /* Local variables. + */ + int nChar; + int nEnd = FALSE; + int nNdx; + int nResult; +/* int nRetBufSize; */ + int nReturn = SDD_OK; + UCHAR *pszTmpBuf; +/* UCHAR *pszRetBuf; */ + UCHAR *szFunc = "_AUPL_Exec"; + FILE *fpStdIn; + FILE *fpStdOut; + + + /* Allocate memory for return buffer. + if((pszRetBuf=(UCHAR *)malloc(nRetBufSize)) == NULL) + { + return(SDD_FAIL); + } + */ + + /* Concatenate the decompressors file name plus the path and filename of + * the audio file to enable the decompression of the audio file to go + * ahead. + */ + nNdx=strlen(AUPL.szDecompExec)+strlen(pszAudioPath)+strlen(pszAudioFile)+10; + if((pszTmpBuf=(UCHAR *)malloc(nNdx)) == NULL) + { + return(SDD_FAIL); + } + sprintf(pszTmpBuf, "%s %s//%s", AUPL.szDecompExec, pszAudioPath, + pszAudioFile); + + /* Execute the program as a child with its stdout connected to a read + * stream such that we can capture its output. + */ + if((fpStdIn=popen(pszTmpBuf, "r")) == NULL) + { + /* Build up an error message to indicate that the decompression + * command failed. + */ + sprintf(szErrMsg, "%s: Failed to de-compress file (%s//%s).", + SDD_EMSG_BADEXEC, pszAudioPath, pszAudioFile); + + /* Set exit code to indicate failure. + */ + nReturn = SDD_FAIL; + } else + if((fpStdOut=popen(AUPL.szAudioPlayer, "w")) == NULL) + { + /* Build up an error message to indicate that the Audio Player + * command failed. + */ + sprintf(szErrMsg, "%s: Failed to start Audio Player.", + SDD_EMSG_BADEXEC); + + /* Set exit code to indicate failure. + */ + nReturn = SDD_FAIL; + } + + while((nChar=fgetc(fpStdIn)) != EOF) + { + fputc(nChar, fpStdOut); + } + + /* Close the Decompressor and get its exit code. + */ + nResult=pclose(fpStdIn); + + /* Close the Audio Player and get its exit code. + */ + nResult=pclose(fpStdOut); + + + /* Free up allocated memory block. + */ + free(pszTmpBuf); + + /* Return result code to caller. + */ + return(nReturn); +} +#endif + +/****************************************************************************** + * Function: aupl_InitService + * Description: Entry point which initialises the driver into a defined state. + * It is mandatory that this function is called before any other + * in order for the driver to function correctly. The caller + * provides it with two types of data, 1) A structure containing + * data for it to use in initialising itself, 2) a pointer to a + * buffer which the driver uses to place an error message should + * it not be able to complete initialisation. + * + * Returns: SDD_FAIL- An error occurred in initialising the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver initialised successfully. + ******************************************************************************/ +int aupl_InitService( SERVICEDETAILS *sServiceDet, /* I: Init data */ + UCHAR *szErrStr ) /* O: Error message */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "aupl_InitService"; + + /* Setup any required constants, variables etc. + */ + strcpy(AUPL.szAudioPlayer, AUPL_AUDIO_PLAYER); + strcpy(AUPL.szDecompExec, AUPL_DECOMPRESSOR); + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "AUPL Driver: Initialised:"); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: aupl_CloseService + * Description: Entry point which performs a drive closedown. The closedown + * procedure ensure that the driver returns to a virgin state + * (ie.like at power up) so that InitService can be called again. + * + * Returns: SDD_FAIL- An error occurred in closing the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver successfully closed. + ******************************************************************************/ +int aupl_CloseService( UCHAR *szErrMsg ) /* O: Error message if failed */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "aupl_CloseService"; + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "AUPL Driver: Closed."); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: aupl_ProcessRequest + * Description: Entry point into driver to initiate the driver into + * processing a request. A data block is passed as a parameter + * to the driver which represents a request with relevant + * parameters. The data within the structure is only relevant + * to the original client and this driver code. + * + * Returns: SDD_FAIL- An error occurred within the driver whilst trying to + * process the request, see error text. + * SDD_OK - Request processed successfully. + ******************************************************************************/ +int aupl_ProcessRequest( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply*/ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Static variables. + */ + + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *pszAudioFile; + UCHAR *pszAudioPath; + UCHAR *szFunc = "aupl_ProcessRequest"; + + /* If the request block doesnt contain any data, something went wrong + * somewhere? + */ + if(nDataLen <= 1) + { + sprintf(szErrMsg, + "%s: Illegal request, has size of %dBytes", + SDD_EMSG_BADREQ, nDataLen); + return(SDD_FAIL); + } + + /* If we have a PLAY_Z command, then extract relevant information from + * buffer. + */ + if(snzDataBuf[0] == SDD_PLAY_Z) + { + /* Locate the path argument and validate it. + */ + if(_AUPL_GetStrArg(&snzDataBuf[1], nDataLen-1, AUPL_AUDIOPATH, + &pszAudioPath) == SDD_FAIL|| + _AUPL_ValidatePath(pszAudioPath) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid PATH to Audio File provided", + SDD_EMSG_BADPATH); + return(SDD_FAIL); + } + + /* Locate the Audio File Name argument and validate it. + */ + if(_AUPL_GetStrArg(&snzDataBuf[1], nDataLen-1, AUPL_AUDIOFILE, + &pszAudioFile) == SDD_FAIL || + _AUPL_ValidateFile(pszAudioPath, pszAudioFile, FALSE) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid Audio Filename provided", + SDD_EMSG_BADFILE); + return(SDD_FAIL); + } + } + + /* First byte of the request data block indicates actions required, so + * decipher it. + */ + switch(snzDataBuf[0]) + { + case SDD_PLAY_Z: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "AUPL Driver: Req to execute Play Compressed File"); + Lgr(LOG_DEBUG, szFunc, + "File Details: Path=%s, File=%s", pszAudioPath, pszAudioFile); + + /* Execute the function. The final exit status dictates wether the + * command was a success or failure. + */ + if(_AUPL_PlayZ(pszAudioPath, pszAudioFile, fSendDataCB, + szErrMsg) == SDD_FAIL) + { + return(SDD_FAIL); + } + break; + + default: + sprintf(szErrMsg, + "%s: Illegal request in request buffer (%x)", + SDD_EMSG_BADREQ, snzDataBuf[0]); + return(SDD_FAIL); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: aupl_ProcessOOB + * Description: Entry point into driver to process an out of band command + * that may or may not be relevant to current state of + * operation. The task of this function is to decipher the + * command and act on it immediately, ie. a cancel command + * would abort any ProcessRequest that is in process and + * clean up. + * + * Returns: No returns. + ******************************************************************************/ +void aupl_ProcessOOB( UCHAR nCommand ) /* I: OOB Command */ +{ + /* Local variables. + */ + + /* Decipher command and perform actions accordingly. + */ + switch(nCommand) + { + /* Request to abort current ProcessRequest command and return daemon + * to a waiting-for-request state. + */ + case SDD_ABORT: + break; + + /* Request to close down and exit. + */ + case SDD_EXIT: + break; + + default: + break; + } + + /* Return to caller. + */ + return; +} diff --git a/SDD/sdd_aupl.h b/SDD/sdd_aupl.h new file mode 100755 index 0000000..f5c3228 --- /dev/null +++ b/SDD/sdd_aupl.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_aupl.h + * Description: Server Data-source Driver library driver header file for + * the audio player driver. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef SDD_AUPL_H +#define SDD_AUPL_H + +/* Definitions for constants, configurable options etc. +*/ +#define AUPL_AUDIOPATH "PATH=" +#define AUPL_AUDIOFILE "FILE=" +#define AUPL_DECOMPRESSOR "/home/vdwd/bin/zcat" +#define AUPL_AUDIO_PLAYER "/home/vdwd/bin/play" + +/* Definitions for maxims and defaults etc. +*/ +#define MAX_AUDIOPLAYER_LEN sizeof(AUPL_AUDIO_PLAYER) +#define MAX_DECOMPRESSOR_LEN sizeof(AUPL_DECOMPRESSOR) + +/* Structure for variables needed within this module. +*/ +typedef struct { + UCHAR szAudioPlayer[MAX_AUDIOPLAYER_LEN+1]; + UCHAR szDecompExec[MAX_DECOMPRESSOR_LEN+1]; +} AUPL_DRIVER; + +/* Allocate global variables by instantiating the above structure. +*/ +static AUPL_DRIVER AUPL; + +/* Prototypes of internal functions, not seen by any outside module. +*/ +int _AUPL_GetStrArg( UCHAR *, int, UCHAR *, UCHAR ** ); +int _AUPL_ValidatePath( UCHAR * ); +int _AUPL_ValidateFile( UCHAR *, UCHAR *, UINT ); +#if defined(SOLARIS) || defined(SUNOS) +int _AUPL_PlayZ(UCHAR *, UCHAR *, int(*)(UCHAR *, UINT), UCHAR * ); +#endif + +#endif /* SDD_AUPL_H */ diff --git a/SDD/sdd_ftpx.c b/SDD/sdd_ftpx.c new file mode 100755 index 0000000..848b04b --- /dev/null +++ b/SDD/sdd_ftpx.c @@ -0,0 +1,2178 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_ftpx.c + * Description: Server Data-source Driver library driver to handle direct + * FTP transfers to an FTP server. + * + * 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 + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) +#include +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define SDD_FTPX_C + +/* Bring in local specific header files. +*/ +#include "sdd.h" +#include "sdd_ftpx.h" + +/****************************************************************************** + * Function: _FTPX_GetStrArg + * Description: Function to scan an input buffer and extract a string based + * argument. + * + * Returns: SDD_FAIL- Couldnt obtain argument. + * SDD_OK - Argument obtained. + ******************************************************************************/ +int _FTPX_GetStrArg( UCHAR *snzDataBuf, /* I: Input buffer */ + int nDataLen, /* I: Len of data */ + UCHAR *szArg, /* I: Arg to look for */ + UCHAR **pszPath ) /* O: Pointer to argument */ +{ + /* Local variables. + */ + int nCmpLen; + int nPos; + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_FTPX_GetStrArg"; + + /* Go through the input buffer looking for 'szArg' within the + * input buffer. If it exists, then note the position just after it + * as this contains the string argument. + */ + for(nPos=0, nCmpLen=strlen(szArg); nPos < nDataLen; nPos++) + { + /* If a match occurs, then setup the pointer to correct location. + */ + if(strncmp(&snzDataBuf[nPos], szArg, nCmpLen) == 0) + { + /* Make sure that the match is not a sub-match as some of the + * variables we are scanning for are similar. + */ + if( (nPos == 0) || + (nPos > 0 && snzDataBuf[nPos-1] == '\0') || + (nPos > 0 && isspace(snzDataBuf[nPos-1])) ) + { + nPos += nCmpLen; + break; + } + } + } + + /* If the pointer did not reach the end of the buffer then we have + * located a valid name. + */ + if(nPos < nDataLen) + { + /* Setup the callers pointer to point to the correct location + * in the buffer. + */ + *pszPath = &snzDataBuf[nPos]; + nReturn=SDD_OK; + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_GetMode + * Description: Function to scan an input buffer and determine the mode + * of FTP operation that the caller requires (Binary or Ascii). If + * no mode is provided then default to binary. + * + * Returns: Mode Flag - 1 = Binary mode selected. + * - 0 = Ascii mode selected. + ******************************************************************************/ +int _FTPX_GetMode( UCHAR *snzDataBuf, /* I: Input buffer */ + int nDataLen ) /* I: Len of data */ +{ + /* Local variables. + */ + int nCmpLen; + int nPos; + int nReturn = FTP_BINARY_MODE; + UCHAR *szFunc = "_FTPX_GetMode"; + + /* Go through the input buffer looking for the MODE directive. If it + * exists then note the position just after it as this is the flag + * value indicating BINARY or ASCII. + */ + for(nPos=0, nCmpLen=strlen(FTP_MODE); nPos < nDataLen; nPos++) + { + /* If a match occurs, then setup the pointer to correct location. + */ + if(strncmp(&snzDataBuf[nPos], FTP_MODE, nCmpLen) == 0) + { + /* Make sure that the match is not a sub-match as some of the + * variables we are scanning for are similar. + */ + if( (nPos == 0) || + (nPos > 0 && snzDataBuf[nPos-1] == '\0') || + (nPos > 0 && isspace(snzDataBuf[nPos-1])) ) + { + nPos += nCmpLen; + break; + } + } + } + + /* If the pointer did not reach the end of the buffer then we have + * located the beginning of the argument list. + */ + if(nPos < nDataLen) + { + /* Does the user require Ascii mode? If he doesnt, then assume binary + * as its pointless error trapping here. + */ + if(strcasecmp(&snzDataBuf[nPos], FTP_ASCII) == 0) + { + nReturn = FTP_ASCII_MODE; + } + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_GetWriteData + * Description: Function to scan an input buffer, verify that it has data in it, + * extract the data and store in the opened file stream and + * set the start flag if the block is the final block. + * + * Returns: SDD_FAIL- Bad block of data or error writing to file. + * SDD_OK - Block obtained and stored. + ******************************************************************************/ +int _FTPX_GetWriteData( UCHAR *snzDataBuf, /* I: Input buffer */ + int nDataLen, /* I: Len of data */ + FILE *fpFile, /* IO: Opened file stream */ + int *nLast, /* O: Last block flag */ + UCHAR *szErrMsg ) /* O: Any resultant error msg */ +{ + /* Local variables. + */ + int nBlockSize; + int nCmpDataLen; + int nCmpEndLen; + int nPos; + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_FTPX_GetWriteData"; + + /* Initialise any required variables. + */ + *nLast = FALSE; + nCmpDataLen=strlen(FTP_DATA); + nCmpEndLen=strlen(FTP_END); + + /* Go through the input buffer looking for FTP_DATA or FTP_END within the + * input buffer. If either exist then note position just after it + * as this contains the size of the data block as an ascii number. If + * FTP_END is detected then set the nLast flag to indicate last block. + */ + for(nPos=0; nPos < nDataLen; nPos++) + { + /* If a match occurs, then setup the pointer to correct location. + */ + if(strncmp(&snzDataBuf[nPos], FTP_DATA, nCmpDataLen) == 0 || + strncmp(&snzDataBuf[nPos], FTP_END, nCmpEndLen) == 0) + { + /* Make sure that the match is not a sub-match as some of the + * variables we are scanning for are similar. + */ + if( (nPos == 0) || + (nPos > 0 && snzDataBuf[nPos-1] == '\0') || + (nPos > 0 && isspace(snzDataBuf[nPos-1])) ) + { + if(strncmp(&snzDataBuf[nPos], FTP_DATA, nCmpDataLen) == 0) + { + nPos += nCmpDataLen; + } else + { + /* Setup the nLast flag as this is the last data block. + */ + *nLast = TRUE; + nPos += nCmpEndLen; + } + break; + } + } + } + + /* If the pointer did not reach the end of the buffer then we have + * located a valid data block. + */ + if(nPos < nDataLen) + { + /* The following location upto a NULL byte should contain the + * size of the data block in ASCII representation so use scanf + * to extract the data accordingly. + */ + if(sscanf(&snzDataBuf[nPos], "%d", &nBlockSize) != 1) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, "%s: Invalid Block Size provided", + SDD_EMSG_BADBLOCKSZ); + nReturn = SDD_FAIL; + goto _FTPX_GetWriteData_EXIT; + } + + /* Update our index variable. + */ + nPos += (strlen(&snzDataBuf[nPos]) + 1); + + /* Quick test before we go any further, ensure that the data + * provided is sufficient. + */ + if((nPos+nBlockSize) != nDataLen) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, + "%s: Data block size does not match data provided", + SDD_EMSG_BADBLOCKSZ); + nReturn = SDD_FAIL; + goto _FTPX_GetWriteData_EXIT; + } + + /* Read data byte at a time and write to the opened file stream. + */ + while(nPos < nDataLen) + { + if(fputc(snzDataBuf[nPos], fpFile) == EOF) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, + "%s: Couldnt write to temporary file to store data", + SDD_EMSG_FILEERROR); + nReturn = SDD_FAIL; + break; + } + + /* Time for pointer update... Could write the same data forever + * I suppose.... + */ + nPos += 1; + } + } + + /* I suppose we are successful.... this time! + */ + nReturn=SDD_OK; + +_FTPX_GetWriteData_EXIT: + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_PutReadData + * Description: Function to read an open stream and transmit the data + * contents to the caller via the callback mechanism. + * + * Returns: SDD_FAIL- Couldnt obtain PATH. + * SDD_OK - PATH obtained. + ******************************************************************************/ +int _FTPX_PutReadData( FILE *fpFile, /* I: Stream to read from */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send data to */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nChar; + int nEnd = FALSE; + int nNdx; + int nReturn; + UCHAR szRetBuf[MAX_RETURN_BUF]; + UCHAR *szFunc = "_FTPX_PutReadData"; + + /* Loop, extracting data from the input stream and sending it to the + * users callback in MAX_RETURN_BUF (ID + MAX_RETURN_BUF-1 bytes data) + * byte blocks until we reach the end. + */ + while(nEnd == FALSE) + { + /* Initialise any loop variables. + */ + nNdx=1; + + /* Loop until the return buffer becomes full or we run out of data. + */ + while(nNdx < (MAX_RETURN_BUF-1) && (nChar=fgetc(fpFile)) != EOF) + { + szRetBuf[nNdx] = (UCHAR)nChar; + nNdx++; + } + + /* Is this the end of the input stream? + */ + if(nChar == EOF) + { + /* Set flag as we've got to end of the file, time to get out. + */ + nEnd = TRUE; + + /* Set the first location in the input buffer to indicate last + * block. + */ + szRetBuf[0] = SDD_ENDBLOCK; + } else + { + /* Set the first location in the input buffer to indicate that + * this is one of many data blocks. + */ + szRetBuf[0] = SDD_DATABLOCK; + } + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(szRetBuf, nNdx) == SDD_FAIL) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", SDD_EMSG_SENDROW); + nReturn = SDD_FAIL; + nEnd = TRUE; + } + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_PIDataCB + * Description: Function to handle any control information passed back from + * the FTP server. + * + * Returns: No returns. + ******************************************************************************/ +void _FTPX_PIDataCB( UINT nChanId, /* I: Channel data arrived on */ + UCHAR *szData, /* I: Actual data */ + UINT nDataLen ) /* I: Length of data */ +{ + /* Local variables. + */ + UINT nA1, nA2, nA3, nA4; + UINT nNdx; + UINT nP1, nP2; + UCHAR *spNewBuf = NULL; + UCHAR *spTmp = NULL; + UCHAR *spTmp2 = NULL; + UCHAR *szFunc = "_FTPX_PIDataCB"; + + /* If needed, create initial FTP buffer. + */ + if(FTPX.snzPIDataBuf == NULL) + { + if((FTPX.snzPIDataBuf=(UCHAR *)malloc(DEF_FTP_BUF_SIZE)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, + "Couldnt malloc initial FTP buffer, wasting data"); + return; + } + + /* Setup initial size. + */ + FTPX.nPIDataBufLen = DEF_FTP_BUF_SIZE; + FTPX.nPIDataBufPos = 0; + } + + /* If the data will overfill our buffer, then grow it to accomodate. + */ + if( (FTPX.nPIDataBufPos+nDataLen) > FTPX.nPIDataBufLen) + { + /* For safety's sake, an upper limit on the size of the + * receive buffer has to be implemented. If this ceiling + * is hit, then assume something is going wrong, so keep + * the current buffer, but dump the contents. + */ + if( FTPX.nPIDataBufLen >= MAX_FTP_BUF_SIZE ) + { + Lgr(LOG_DEBUG, szFunc, + "Exceeded maximum size of recv buffer, dumping"); + FTPX.nPIDataBufPos = 0; + return; + } + + /* OK, lets grow the buffer by a fixed size. If no memory + * is available, then keep the current buffer, just stop + * reading. Later functionality will prune the data. + */ + if((spNewBuf=(UCHAR *)malloc(FTPX.nPIDataBufLen+DEF_FTP_BUF_INCSIZE)) + == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + DEF_FTP_BUF_INCSIZE); + return; + } + + /* Copy the old buffer onto the new, so we can release it + * back to the sys pool. + */ + for(spTmp=FTPX.snzPIDataBuf, spTmp2=spNewBuf, nNdx=FTPX.nPIDataBufLen; + nNdx != 0; nNdx--, *spTmp2 = *spTmp, spTmp++, spTmp2++); + + /* Get rid of old buffer, and update pointers to accomodate the new + * buffer and new size values. + */ + free(FTPX.snzPIDataBuf); + FTPX.snzPIDataBuf = spNewBuf; + FTPX.nPIDataBufLen += DEF_FTP_BUF_INCSIZE; + + /* Log a message indicating that the buffer has grown in size. + */ + Lgr(LOG_DEBUG, szFunc, + "Allocated new receive buffer of %d bytes", FTPX.nPIDataBufLen); + } + + /* Copy data from passed buffer into our buffer. + */ + for(nNdx=FTPX.nPIDataBufPos; nNdx < (FTPX.nPIDataBufPos+nDataLen); nNdx++) + { + FTPX.snzPIDataBuf[nNdx] = szData[nNdx-FTPX.nPIDataBufPos]; + } + FTPX.nPIDataBufPos += nNdx; + + /* Scan through the buffer looking for the start of a number as + * this will represent the FTP Server response code. + */ + for(nNdx=0; nNdx < FTPX.nPIDataBufPos; nNdx++) + { + if(isdigit(FTPX.snzPIDataBuf[nNdx])) + break; + } + + /* Digit detected? If not, fall through and wait for more data to + * arrive. + */ + if(nNdx < FTPX.nPIDataBufPos) + { + /* Get number. + */ + sscanf(&FTPX.snzPIDataBuf[nNdx], "%d", &FTPX.nPIResponseCode); + + /* Special case handling if weve entered passive mode. + */ + if(FTPX.nPIResponseCode == FTP_RESPONSE_PASSIVE) + { + /* Extract the IP address and port number from the response. + */ + if(sscanf(&FTPX.snzPIDataBuf[nNdx+27], "%d,%d,%d,%d,%d,%d", + &nA1, &nA2, &nA3, &nA4, &nP1, &nP2) == 6) + { + /* Build up address and port number in useable form. + */ + FTPX.lDTPIPaddr = ((ULNG)nA1*16777216L) + ((ULNG)nA2*65536L) + + ((ULNG)nA3*256L) + (ULNG)nA4; + FTPX.nDTPPortNo = (nP1*256) + nP2; + } else + { + FTPX.nPIResponseCode == FTP_RESPONSE_NONE; + } + } + + /* Clear buffer, data no longer needed. + */ + FTPX.nPIDataBufPos = 0; + } + + /* Return to caller. + */ + return; +} + +/****************************************************************************** + * Function: _FTPX_PICtrlCB + * Description: Function to handle any control callbacks during connectivity + * with the FTP server. + * + * Returns: No returns. + ******************************************************************************/ +void _FTPX_PICtrlCB( int nType, /* I: Type of callback */ + ... ) /* I: Var args */ +{ + /* Local variables. + */ + UINT nChanId; + UINT nPortNo; + ULNG lIPaddr; + va_list pArgs; + UCHAR *szFunc = "_FTPX_PICtrlCB"; + + /* 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: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + /* Log link going down event for bug catching. + */ + Lgr(LOG_DEBUG, szFunc, + "New Service with client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + break; + + /* A connection has been made + */ + case SLC_CONNECT: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + Lgr(LOG_DEBUG, szFunc, + "Connected to client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + + SL_RawMode(nChanId, TRUE); + break; + + /* Given connection has become temporarily unavailable. + */ + case SLC_LINKDOWN: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + /* Log link going down event for bug catching. + */ + Lgr(LOG_DEBUG, szFunc, + "Link down to client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + + /* Get rid of connection, no longer needed. + */ + SL_DelClient(FTPX.nPIChanId); + FTPX.nPIChanId = 0; + break; + + /* Given connection has died. + */ + case SLC_LINKFAIL: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + /* Log link failure event for bug catching. + */ + Lgr(LOG_DEBUG, szFunc, + "Link closed to client: nChanId=%d, nPortNo=%d, IP=%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; + } + + /* Return to caller. + */ + return; +} + +/****************************************************************************** + * Function: _FTPX_DTPDataCB + * Description: Function to handle any data passed back from the FTP server on + * the data transfer connection.. + * + * Returns: No returns. + ******************************************************************************/ +void _FTPX_DTPDataCB( UINT nChanId, /* I: Channel data arrived on */ + UCHAR *szData, /* I: Actual data */ + UINT nDataLen ) /* I: Length of data */ +{ + /* Local variables. + */ + UINT nNdx; + UCHAR *szFunc = "_FTPX_DTPDataCB"; + + /* Simply store the data into our opened file buffer. + */ + if(FTPX.fDataFile == NULL) + { + return; + } + + /* Copy data byte at a time into the storage file. + */ + for(nNdx=0; nNdx < nDataLen; nNdx++) + { + fputc(szData[nNdx], FTPX.fDataFile); + } + + /* Return to caller. + */ + return; +} + +/****************************************************************************** + * Function: _FTPX_DTPCtrlCB + * Description: Function to handle any control callbacks on the Data Transfer + * connection with the FTP server. + * + * Returns: No returns. + ******************************************************************************/ +void _FTPX_DTPCtrlCB( int nType, /* I: Type of callback */ + ... ) /* I: Var args */ +{ + /* Local variables. + */ + UINT nChanId; + UINT nPortNo; + ULNG lIPaddr; + va_list pArgs; + UCHAR *szFunc = "_FTPX_DTPCtrlCB"; + + /* 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: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + /* Log link going down event for bug catching. + */ + Lgr(LOG_DEBUG, szFunc, + "New Service with client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + + /* Set channel into raw mode. + */ + SL_RawMode(nChanId, TRUE); + + /* Store the channel id, because a server connection only + * liberates the channel id at this point. + */ + FTPX.nDTPChanId=nChanId; + FTPX.nDTPConnected=TRUE; + break; + + /* A connection has been made + */ + case SLC_CONNECT: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + Lgr(LOG_DEBUG, szFunc, + "Connected to client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + + SL_RawMode(nChanId, TRUE); + FTPX.nDTPConnected = TRUE; + break; + + /* Given connection has become temporarily unavailable. + */ + case SLC_LINKDOWN: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + /* Log link going down event for bug catching. + */ + Lgr(LOG_DEBUG, szFunc, + "Link down to client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + + /* Mark channel as closed. + */ + FTPX.nDTPConnected = FALSE; + + /* On a link down, remove the connection entry as its no longer + * needed. + */ + SL_DelClient(FTPX.nDTPChanId); + FTPX.nDTPChanId = 0; + break; + + /* Given connection has died. + */ + case SLC_LINKFAIL: + nChanId = va_arg(pArgs, UINT); + nPortNo = va_arg(pArgs, UINT); + lIPaddr = va_arg(pArgs, ULNG); + + /* Log link failure event for bug catching. + */ + Lgr(LOG_DEBUG, szFunc, + "Link closed to client: nChanId=%d, nPortNo=%d, IP=%s", + nChanId, nPortNo, SL_HostIPtoString(lIPaddr)); + + /* Mark channel as closed. + */ + FTPX.nDTPConnected = FALSE; + break; + + default: + /* Log a message as this condition shouldnt occur. + */ + Lgr(LOG_DEBUG, szFunc, "Unrecognised message type (%d)", nType); + break; + } + + /* Return to caller. + */ + return; +} + +/****************************************************************************** + * Function: _FTPX_PIGetResponse + * Description: Function to get a response code from the FTP server. + * + * Returns: Response Code. + ******************************************************************************/ +int _FTPX_PIGetResponse( void ) +{ + /* Local variables. + */ + UINT nTotalTime; + UCHAR *szFunc = "_FTPX_PIGetResponse"; + + /* Setup what type of data we are looking for from FTP server. + */ + FTPX.nPIResponseCode = FTP_RESPONSE_NONE; + + /* OK, lets go into a loop state until we get a required response. + */ + nTotalTime = 0; + while(FTP_CONNECT_TIME > nTotalTime && + FTPX.nPIResponseCode==FTP_RESPONSE_NONE) + { + SL_Poll((ULNG)1); + nTotalTime += 1; + } + + /* Return to caller. + */ + return(FTPX.nPIResponseCode); +} + +/****************************************************************************** + * Function: _FTPX_PISendCmd + * Description: Function to send a command to an FTP server. + * + * Returns: SDD_FAIL - FTP Server failed to respond, see szErrMsg. + * SDD_OK - Command sent successfully. + ******************************************************************************/ +int _FTPX_PISendCmd( UCHAR *szCmd, /* I: Command to send */ + UINT *panReqResponses, /* I: Array of req resp */ + UCHAR *szErrMsg ) /* O: Error message */ +{ + /* Local variables. + */ + UINT nNdx; + UINT nResponse; + UINT nReturn = SDD_FAIL; + UCHAR *szFunc = "_FTPX_PISendCmd"; + + /* Send command to FTP server. + */ + if(SL_BlockSendData(FTPX.nPIChanId, (UCHAR *)szCmd, strlen(szCmd)) != R_OK) + { + sprintf(szErrMsg, "Couldnt send command '%s' to FTP server", szCmd); + return(SDD_FAIL); + } + + /* If caller requires a certain set of response to the issued command, then + * obtain a response and match it. + */ + if(panReqResponses == NULL || panReqResponses[0] != FTP_RESPONSE_NONE) + { + /* Get the response from the executed command. + */ + nResponse=_FTPX_PIGetResponse(); + + /* See if the response exists in the array that the caller has + * passed. + */ + for(nNdx=0; nResponse != FTP_RESPONSE_NONE && + panReqResponses[nNdx] != FTP_RESPONSE_NONE; nNdx++) + { + if(panReqResponses[nNdx] == nResponse) + break; + } + + /* If there was no response or rthe response did not match any of + * the callers then indicate error. + */ + if(nResponse == FTP_RESPONSE_NONE || + panReqResponses[nNdx] == FTP_RESPONSE_NONE) + { + sprintf(szErrMsg, "Illegal response '%d' obtained for command '%s'", + nResponse, szCmd); + } else + { + nReturn = SDD_OK; + } + } else + { + nReturn = SDD_OK; + } + + /* Return to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_PIGetDTPResponse + * Description: Function to get a response code during a DTP transfer. + * + * Returns: Response Code. + ******************************************************************************/ +int _FTPX_PIGetDTPResponse( void ) +{ + /* Local variables. + */ + UINT nTotalTime; + UCHAR *szFunc = "_FTPX_PIGetResponse"; + + /* Setup what type of data we are looking for from FTP server. + */ + FTPX.nPIResponseCode = FTP_RESPONSE_NONE; + + /* OK, lets go into a loop state until we get a required response. + */ + nTotalTime = 0; + while( FTP_CONNECT_TIME > nTotalTime ) + { + SL_Poll((ULNG) 1); + nTotalTime += 1; + + if(FTPX.nPIResponseCode!=FTP_RESPONSE_NONE && FTPX.nDTPConnected==FALSE) + break; + } + + /* Return to caller. + */ + return(FTPX.nPIResponseCode); +} + + +/****************************************************************************** + * Function: _FTPX_PISendDTPCmd + * Description: Function to send a command to an FTP server which will invoke + * a DTP channel for data transfer. + * + * Returns: SDD_FAIL - FTP Server failed to respond, see szErrMsg. + * SDD_OK - Command sent successfully. + ******************************************************************************/ +int _FTPX_PISendDTPCmd( UCHAR *szCmd, /* I: Command to send */ + UINT *panReqResponses, /* I: Allowed responses */ + UCHAR *szErrMsg ) /* O: Error message */ +{ + /* Local variables. + */ + UINT anResponses[3]; + UINT nPortNo; + UINT nReturn = SDD_OK; + UINT nTotalTime; + ULNG lIPAddr; + UCHAR szTmp[MAX_TMP_BUF_SIZE]; + UCHAR *szFunc = "_FTPX_PISendDTPCmd"; + + /* Setup allowed responses. + */ + anResponses[0] = FTP_RESPONSE_PASSIVE; + anResponses[1] = FTP_RESPONSE_NONE; + + /* Tel FTP server to enter into passive mode. + */ + if(_FTPX_PISendCmd("PASV\r\n", anResponses, szErrMsg) == SDD_OK) + { + /* OK, FTP server has entered passive mode, so lets build a Data + * Transfer link to it. + */ + if((FTPX.nDTPChanId = SL_AddClient(FTPX.nDTPPortNo, FTPX.lDTPIPaddr, + "DTP Channel", _FTPX_DTPDataCB, + _FTPX_DTPCtrlCB)) < 0) + { + sprintf(szErrMsg, "Couldnt build DTP connection"); + return(SDD_FAIL); + } + + /* Reset flag so that when connection is built we can detect it. + */ + FTPX.nDTPConnected = FALSE; + + /* OK, lets go into a loop state until we get a required response. + */ + nTotalTime = 0; + while(FTP_CONNECT_TIME > nTotalTime && FTPX.nDTPConnected == FALSE) + { + SL_Poll((ULNG) 1); + nTotalTime += 1; + } + + /* Was the connection made? + */ + if(FTPX.nDTPConnected == FALSE) + { + sprintf(szErrMsg, "Couldnt build DTP connection"); + return(SDD_FAIL); + } + } else + { + /* Look for a port we can hang on to. + */ + for(nPortNo=DEF_SRV_START_PORT; nPortNo < DEF_SRV_END_PORT; nPortNo++) + { + /* Try and add a service on this port, if ok, then exit and run + * with it. + */ + if(SL_AddServer(nPortNo, FALSE, _FTPX_DTPDataCB, + _FTPX_DTPCtrlCB) == SDD_OK) + break; + } + + /* Have we reached the limit? If we have, give up, saturated server + * or what!! + */ + if(nPortNo == DEF_SRV_END_PORT) + { + sprintf(szErrMsg, "Couldnt add a service port for DTP channel"); + return(SDD_FAIL); + } + + /* OK, lets tell the FTP server that we are listening for a + * connection. Firstly, work out who we are. + */ + gethostname(szTmp, MAX_TMP_BUF_SIZE); + if(SL_GetIPaddr(szTmp, &lIPAddr) != R_OK) + { + sprintf(szErrMsg, "Couldnt get our IP address"); + return(SDD_FAIL); + } + + /* Convert the IP address and Port number into easy blocks for + * inserting into our command. + */ + PutCharFromLong(szTmp, lIPAddr); + PutCharFromInt(&szTmp[4], nPortNo); + + /* Build up the command to instruct the FTP server. + */ + sprintf(&szTmp[6], "PORT %d,%d,%d,%d,%d,%d\r\n", + (UINT)szTmp[0], (UINT)szTmp[1], (UINT)szTmp[2], + (UINT)szTmp[3], (UINT)szTmp[4], (UINT)szTmp[5]); + + /* Setup allowed responses. + */ + anResponses[0] = FTP_RESPONSE_CWDOK; + anResponses[1] = FTP_RESPONSE_CMDOK; + anResponses[2] = FTP_RESPONSE_NONE; + + /* Fire the command at the server and stand back.....Ce'st la vie. + */ + if(_FTPX_PISendCmd(&szTmp[6], anResponses, szErrMsg) == SDD_FAIL) + { + sprintf(szErrMsg, "Couldnt instruct FTP server to use port %d", + nPortNo); + return(SDD_FAIL); + } + } + + /* Send command to FTP server + */ + if(_FTPX_PISendCmd(szCmd, panReqResponses, szErrMsg) != SDD_OK) + { + sprintf(&szErrMsg[strlen(szErrMsg)], + ": Failed to obtain valid response from FTP server"); + return(SDD_FAIL); + } + + /* Return to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_SetMode + * Description: Function to setup the transfer mode between the FTP server and + * the driver. + * + * Returns: SDD_FAIL - Failed to set transfer mode, critical error. + * SDD_OK - Mode set. + ******************************************************************************/ +int _FTPX_SetMode( UINT nBinaryMode, /* I: Select binary mode = TRUE */ + UCHAR *szErrMsg ) /* O: Generated error messages */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UINT anResponses[3]; + UCHAR *szFunc = "_FTPX_SetMode"; + + /* Setup allowed responses. + */ + anResponses[0] = FTP_RESPONSE_CMDOK; + anResponses[1] = FTP_RESPONSE_CWDOK; + anResponses[2] = FTP_RESPONSE_NONE; + + /* Enable binary mode or ascii? + */ + if( nBinaryMode == TRUE) + { + nReturn=_FTPX_PISendCmd("TYPE I\r\n", anResponses, szErrMsg); + } else + { + nReturn=_FTPX_PISendCmd("TYPE A\r\n", anResponses, szErrMsg); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_SetCwd + * Description: Function to set the FTP servers current working directory. + * + * Returns: SDD_FAIL - Failed to set directory to that specified. + * SDD_OK - Current Working Directory set. + ******************************************************************************/ +int _FTPX_SetCwd( UCHAR *szPath, /* I: Path to set CWD */ + UCHAR *szErrMsg ) /* O: Generated error messages */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UINT anResponses[3]; + UCHAR *pszCmd; + UCHAR *szFunc = "_FTPX_SetCwd"; + + /* Allocate required memory for the command buffer. + */ + if((pszCmd=(UCHAR *)malloc(strlen(szPath)+7)) == NULL) + { + sprintf(szErrMsg, "Couldnt allocate memory for CWD command"); + return(SDD_FAIL); + } + + /* Setup command to change the remote directory to that required. + */ + sprintf(pszCmd, "CWD %s\r\n", szPath); + + /* Setup allowed responses. + */ + anResponses[0] = FTP_RESPONSE_CMDOK; + anResponses[1] = FTP_RESPONSE_CWDOK; + anResponses[2] = FTP_RESPONSE_NONE; + + /* Send command to FTP server. + */ + nReturn=_FTPX_PISendCmd(pszCmd, anResponses, szErrMsg); + + /* Free up resources, no longer needed. + */ + free(pszCmd); + + /* If a failure occurred, tag on our reason code as well. + */ + if(nReturn == SDD_FAIL) + { + sprintf(&szErrMsg[strlen(szErrMsg)], + ": Couldnt change CWD to '%' on remote", szPath); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_FTPInit + * Description: Function to initialise a connection with an FTP server. The + * caller provides the name/IP address of the server and the + * user name/password to complete the connection. + * + * Returns: SDD_FAIL - Couldnt make connection with given details. + * SDD_OK - FTP connection made. + ******************************************************************************/ +int _FTPX_FTPInit( UCHAR *szFTPServer, /* I: Name of FTP server */ + UCHAR *szUserName, /* I: User name to login with */ + UCHAR *szPassword, /* I: Password for login */ + UCHAR *szErrMsg ) /* O: Error message if failed */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UINT anResponses[2]; + UINT nFTPPortNo; + ULNG lIPAddr; + UCHAR *pszCmd; + UCHAR *szFunc = "_FTPX_FTPInit"; + + /* Setup any module variables to an initial state. + */ + FTPX.nPIResponseCode = FTP_RESPONSE_NONE; + FTPX.nPIChanId = 0; + FTPX.nDTPChanId = 0; + FTPX.nPIDataBufLen = 0; + FTPX.nPIDataBufPos = 0; + FTPX.snzPIDataBuf = NULL; + FTPX.lDTPIPaddr = 0; + FTPX.nDTPPortNo = 0; + FTPX.nDTPConnected = FALSE; + FTPX.fDataFile = NULL; + + /* Get the IP address of the host we are connecting with. + */ + if(SL_GetIPaddr(szFTPServer, &lIPAddr) != R_OK) + { + sprintf(szErrMsg, "Server %s does not appear in hosts file", + szFTPServer); + return(SDD_FAIL); + } + + /* Lookup port number for FTP service. + */ + if(SL_GetService(DEF_FTP_SERVICE_NAME, &nFTPPortNo) != R_OK) + { + sprintf(szErrMsg, "FTP service '%s' does not appear in services file", + DEF_FTP_SERVICE_NAME); + return(SDD_FAIL); + } + + /* OK, got IP address and port number, so lets build a connection to + * it. + */ + if((FTPX.nPIChanId = SL_AddClient(nFTPPortNo, lIPAddr, szFTPServer, + _FTPX_PIDataCB, _FTPX_PICtrlCB)) < 0) + { + sprintf(szErrMsg, "Couldnt build IP connection to %s", szFTPServer); + return(SDD_FAIL); + } + + /* Have we connected? + */ + if(_FTPX_PIGetResponse() != FTP_RESPONSE_CONNECTED) + { + sprintf(szErrMsg, "Couldnt build IP connection to %s", szFTPServer); + return(SDD_FAIL); + } + + /* Allocate required memory for the command buffer. + */ + if((pszCmd=(UCHAR *)malloc(strlen(szUserName)+strlen(szPassword)+8))==NULL) + { + sprintf(szErrMsg, "Couldnt allocate memory during FTP initialisation"); + return(SDD_FAIL); + } + + /* OK, send the FTP server the User name. + */ + sprintf(pszCmd, "USER %s\r\n", szUserName); + + /* Setup allowed responses. + */ + anResponses[0] = FTP_RESPONSE_USEROK; + anResponses[1] = FTP_RESPONSE_NONE; + + /* Send command to FTP server. + */ + if(_FTPX_PISendCmd(pszCmd, anResponses, szErrMsg) != SDD_OK) + { + sprintf(&szErrMsg[strlen(szErrMsg)], + ": Couldnt login to FTP server with user '%s'", szUserName); + free(pszCmd); + return(SDD_FAIL); + } + + /* OK, send the FTP server the Password for above user name. + */ + sprintf(pszCmd, "PASS %s\r\n", szPassword); + + /* Setup allowed responses. + */ + anResponses[0] = FTP_RESPONSE_PWDOK; + anResponses[1] = FTP_RESPONSE_NONE; + + /* Send command to FTP server. + */ + nReturn=_FTPX_PISendCmd(pszCmd, anResponses, szErrMsg); + + /* Free up memory buffer, no longer needed. + */ + free(pszCmd); + + /* If a failure occurred on last command, tag on our reason code. + */ + if(nReturn == SDD_FAIL) + { + sprintf(&szErrMsg[strlen(szErrMsg)], + ": Couldnt login to FTP server with password '%s'", szPassword); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_FTPClose + * Description: Function to close a connected FTP connection and tidy up + * in preparation for next task. + * + * Returns: SDD_FAIL - Failed to close properly, library wont work again. + * SDD_OK - Closed. + ******************************************************************************/ +int _FTPX_FTPClose( UCHAR *szErrMsg ) /* O: Generated error messages */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UINT anResponses[2]; + UCHAR *szFunc = "_FTPX_FTPClose"; + + /* If the PI channel is open, close it by issuing the QUIT command to + * the FTP server then deleting the connection. + */ + if(FTPX.nPIChanId != 0) + { + /* Setup allowed responses. + */ + anResponses[0] = FTP_RESPONSE_GOODBYE; + anResponses[1] = FTP_RESPONSE_NONE; + + /* Send command to FTP server. + */ + _FTPX_PISendCmd("QUIT\r\n", anResponses, szErrMsg); + + /* Delete the physical connection. + */ + SL_DelClient(FTPX.nPIChanId); + FTPX.nPIChanId = 0; + } + + /* If the DTP channel is open (shouldnt be!!!), delete the connection. + */ + if(FTPX.nDTPChanId != 0) + { + /* Delete the physical connection. + */ + SL_DelClient(FTPX.nDTPChanId); + FTPX.nDTPChanId = 0; + } + + /* Free the PI data buffer if still allocated. + */ + if(FTPX.snzPIDataBuf != NULL) + { + free(FTPX.snzPIDataBuf); + } + + /* If the data file is still open, close it and reset. + */ + if(FTPX.fDataFile != NULL) + { + fclose(FTPX.fDataFile); + FTPX.fDataFile = NULL; + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_FTPRenFile + * Description: Function to rename a file on a remote FTP server. + * + * Returns: SDD_FAIL - Failed to rename the required file. + * SDD_OK - File renamed successfully. + ******************************************************************************/ +int _FTPX_FTPRenFile( UCHAR *szPath, /* I: Path to remote file */ + UCHAR *szSrcFile, /* I: Original remote file name */ + UCHAR *szDstFile, /* I: New remote file name */ + UCHAR *szErrMsg ) /* O: Generated error messages */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UINT anResponses[3]; + UCHAR *pszCmd; + UCHAR *szFunc = "_FTPX_FTPRenFile"; + + /* Change to correct directory on remote. + */ + if(_FTPX_SetCwd(szPath, szErrMsg) == SDD_FAIL) + { + return(SDD_FAIL); + } + + /* Allocate required memory for the command buffer. + */ + if((pszCmd=(UCHAR *)malloc(strlen(szSrcFile)+strlen(szDstFile)+10))==NULL) + { + sprintf(szErrMsg, "Couldnt allocate memory for command buffer"); + return(SDD_FAIL); + } + + /* Setup command to issue the FROM portion of the rename command. + */ + sprintf(pszCmd, "RNFR %s\r\n", szSrcFile); + + /* Setup allowed responses. + */ + anResponses[0] = FTP_RESPONSE_RENFROK; + anResponses[1] = FTP_RESPONSE_NONE; + + /* Issue part 1 of the rename sequence. + */ + if(_FTPX_PISendCmd(pszCmd, anResponses, szErrMsg) == SDD_FAIL) + { + sprintf(&szErrMsg[strlen(szErrMsg)], + ": FTP server couldnt locate file '%s'", szSrcFile); + free(pszCmd); + return(SDD_FAIL); + } + + /* Setup command to issue the TO portion of the rename command. + */ + sprintf(pszCmd, "RNTO %s\r\n", szDstFile); + + /* Setup allowed responses. + */ + anResponses[0] = FTP_RESPONSE_CWDOK; + anResponses[1] = FTP_RESPONSE_CMDOK; + anResponses[2] = FTP_RESPONSE_NONE; + + /* Send command to FTP server. + */ + nReturn=_FTPX_PISendCmd(pszCmd, anResponses, szErrMsg); + + /* Free up command buffer, no longer needed. + */ + free(pszCmd); + + /* If the above command failed, tag on our reason code and exit. + */ + if(nReturn == SDD_FAIL) + { + sprintf(&szErrMsg[strlen(szErrMsg)], + ": FTP server couldnt rename to file '%s'", szDstFile); + return(SDD_FAIL); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_FTPRcvFile + * Description: Function to initiate a file transfer from the FTP server + * to the current machine file system. + * + * Returns: SDD_FAIL - Failed to complete file transfer. + * SDD_OK - File received successfully. + ******************************************************************************/ +int _FTPX_FTPRcvFile( UCHAR *szRcvFile, /* I: Name of file to store in */ + UCHAR *szPath, /* I: Path to remote file */ + UCHAR *szFile, /* I: Remote file */ + UINT nBinaryMode, /* I: Select binary transfer mode */ + UCHAR *szErrMsg ) /* O: Generated error messages */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UINT anResponses[2]; + UCHAR *pszCmd; + UCHAR *szFunc = "_FTPX_FTPRcvFile"; + + /* Setup transfer mode. + */ + if(_FTPX_SetMode(nBinaryMode, szErrMsg) == SDD_FAIL) + { + return(SDD_FAIL); + } + + /* Change to correct directory on remote. + */ + if(_FTPX_SetCwd(szPath, szErrMsg) == SDD_FAIL) + { + return(SDD_FAIL); + } + + /* Open file to store data into. + */ + if((FTPX.fDataFile=fopen(szRcvFile, "w")) == NULL) + { + sprintf(szErrMsg, "Could not open '%s' for writing", szRcvFile); + return(SDD_FAIL); + } + + /* Allocate required memory for the command buffer. + */ + if((pszCmd=(UCHAR *)malloc(strlen(szFile)+8))==NULL) + { + sprintf(szErrMsg, "Couldnt allocate memory for command buffer"); + return(SDD_FAIL); + } + + /* Setup command to retrieve the required file. + */ + sprintf(pszCmd, "RETR %s\r\n", szFile); + + /* Setup required responses. + */ + anResponses[0] = FTP_RESPONSE_DATASTART; + anResponses[1] = FTP_RESPONSE_NONE; + + /* Send command to FTP server. + */ + nReturn=_FTPX_PISendDTPCmd(pszCmd, anResponses, szErrMsg); + + /* Free up command buffer, no longer needed. + */ + free(pszCmd); + + /* If the above command failed, tag on our reason code and exit. + */ + if(nReturn == SDD_FAIL) + { + sprintf(&szErrMsg[strlen(szErrMsg)], + ": Failed to receive data file '%s'", szFile); + return(SDD_FAIL); + } + + /* Have we connected? + */ + if(_FTPX_PIGetDTPResponse() != FTP_RESPONSE_DATAEND) + { + sprintf(szErrMsg, "Failure to receive data file '%s'", szFile); + return(SDD_FAIL); + } + + /* OK, all done, so close the file and prepare to exit. + */ + fclose(FTPX.fDataFile); + FTPX.fDataFile=NULL; + + /* Ensure that the link between us and FTP server is severed. + */ + SL_DelClient(FTPX.nDTPChanId); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _FTPX_FTPXmitFile + * Description: Function to initiate a file transfer from the current machine + * file system to the FTP server. + * + * Returns: SDD_FAIL - Failed to complete file transfer. + * SDD_OK - File transmitted successfully. + ******************************************************************************/ +int _FTPX_FTPXmitFile( UCHAR *szXmitFile, /* I: Name of file to transmit */ + UCHAR *szPath, /* I: Path to remote destination */ + UCHAR *szFile, /* I: Remote file */ + UINT nBinaryMode, /* I: Select binary transfer Mode */ + UCHAR *szErrMsg ) /* O: Generated error messages */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UINT nChar; + UINT nNdx; + UINT anResponses[2]; + UINT nSending; + UCHAR *pszCmd; + UCHAR *pszDataBuf; + UCHAR *szFunc = "_FTPX_FTPXmitFile"; + + /* Setup transfer mode. + */ + if(_FTPX_SetMode(nBinaryMode, szErrMsg) == SDD_FAIL) + { + return(SDD_FAIL); + } + + /* Change to correct directory on remote. + */ + if(_FTPX_SetCwd(szPath, szErrMsg) == SDD_FAIL) + { + return(SDD_FAIL); + } + + /* Open the file for transmission. + */ + if((FTPX.fDataFile=fopen(szXmitFile, "r")) == NULL) + { + sprintf(szErrMsg, "Could not open '%s' for reading", szXmitFile); + return(SDD_FAIL); + } + + /* Allocate required memory for the command buffer. + */ + if((pszCmd=(UCHAR *)malloc(strlen(szFile)+8))==NULL) + { + sprintf(szErrMsg, "Couldnt allocate memory for command buffer"); + return(SDD_FAIL); + } + + /* Setup command to store the required file. + */ + sprintf(pszCmd, "STOR %s\r\n", szFile); + + /* Setup required responses. + */ + anResponses[0] = FTP_RESPONSE_DATASTART; + anResponses[1] = FTP_RESPONSE_NONE; + + /* Send command to FTP server. + */ + nReturn=_FTPX_PISendDTPCmd(pszCmd, anResponses, szErrMsg); + + /* Free up command buffer, no longer needed. + */ + free(pszCmd); + + /* If last command failed, tag on our reason code and exit. + */ + if(nReturn == SDD_FAIL) + { + sprintf(&szErrMsg[strlen(szErrMsg)], + ": Failed to transmit data file '%s'", szFile); + return(SDD_FAIL); + } + + /* Allocate required memory for the transmit data buffer. + */ + if((pszDataBuf=(UCHAR *)malloc(DEF_FTP_XMIT_SIZE+1))==NULL) + { + sprintf(szErrMsg, "Couldnt allocate memory for xmit data buffer"); + return(SDD_FAIL); + } + + /* Transmit the contents of the file by going in a tight loop and sending + * packets of a predetermined size to the FTP server. + */ + for(nSending=TRUE, nNdx=0; nSending == TRUE; nNdx=0) + { + /* Fill our buffer with data from the file, cutting short if the + * file end is reached. + */ + while(nNdx < DEF_FTP_XMIT_SIZE && (nChar=fgetc(FTPX.fDataFile)) != EOF) + { + pszDataBuf[nNdx] = (UCHAR)nChar; + nNdx++; + } + + /* If we have reached the end of the file then set the flag such + * that we fall out of this loop. + */ + if(nChar == EOF) + { + nSending=FALSE; + } + + /* If there is any data in the buffer, then send it, regardless of + * wether we are at the end of the file. + */ + if(nNdx > 0) + { + if(SL_BlockSendData(FTPX.nDTPChanId,(UCHAR *)pszDataBuf,nNdx)!=R_OK) + { + sprintf(szErrMsg, "Couldnt send data to FTP server"); + free(pszDataBuf); + return(SDD_FAIL); + } + } + } + + /* Free up memory, no longer needed. + */ + free(pszDataBuf); + + /* Close the DTP channel, which in turn indicates to the FTP server that + * data transmission is complete. + */ + SL_DelClient(FTPX.nDTPChanId); + FTPX.nDTPChanId = 0; + FTPX.nDTPConnected=FALSE; + + /* Wait for the end of data transmission response from the server. If it + * doesnt arrive or another response arrives in its place then something + * went wrong. + */ + if(_FTPX_PIGetDTPResponse() != FTP_RESPONSE_DATAEND) + { + sprintf(szErrMsg, "Failed to send data file '%s'", szFile); + return(SDD_FAIL); + } + + /* OK, all done, so close the file and prepare to exit. + */ + fclose(FTPX.fDataFile); + FTPX.fDataFile=NULL; + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: ftpx_InitService + * Description: Entry point which initialises the driver into a defined state. + * It is mandatory that this function is called before any other + * in order for the driver to function correctly. The caller + * provides it with two types of data, 1) A structure containing + * data for it to use in initialising itself, 2) a pointer to a + * buffer which the driver uses to place an error message should + * it not be able to complete initialisation. + * + * Returns: SDD_FAIL- An error occurred in initialising the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver initialised successfully. + ******************************************************************************/ +int ftpx_InitService( SERVICEDETAILS *sServiceDet, /* I: Init data */ + UCHAR *szErrStr ) /* O: Error message */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "ftpx_InitService"; + + /* Prepare error string. + */ + sprintf(szErrStr, "%s: ", SDD_EMSG_SRCINIT); + + /* Initialise a connection with the FTP server using the data provided + * in the service details record. + */ + if(_FTPX_FTPInit(sServiceDet->uServiceInfo.sFTPInfo.szServer, + sServiceDet->uServiceInfo.sFTPInfo.szUser, + sServiceDet->uServiceInfo.sFTPInfo.szPassword, + &szErrStr[strlen(szErrStr)]) == SDD_FAIL) + { + return(SDD_FAIL); + } + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "FTPX Driver: Initialised:"); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: ftpx_CloseService + * Description: Entry point which performs a drive closedown. The closedown + * procedure ensure that the driver returns to a virgin state + * (ie.like at power up) so that InitService can be called again. + * + * Returns: SDD_FAIL- An error occurred in closing the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver successfully closed. + ******************************************************************************/ +int ftpx_CloseService( UCHAR *szErrMsg ) /* O: Error message if failed */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "ftpx_CloseService"; + + /* Prepare error string. + */ + sprintf(szErrMsg, "%s: ", SDD_EMSG_BADEXIT); + + /* Close connection with FTP server and tidy up. + */ + if(_FTPX_FTPClose( &szErrMsg[strlen(szErrMsg)] ) == SDD_FAIL) + { + return(SDD_FAIL); + } + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "FTPX Driver: Closed."); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: ftpx_ProcessRequest + * Description: Entry point into driver to initiate the driver into + * processing a request. A data block is passed as a parameter + * to the driver which represents a request with relevant + * parameters. The data within the structure is only relevant + * to the original client and this driver code. + * + * Returns: SDD_FAIL- An error occurred within the driver whilst trying to + * process the request, see error text. + * SDD_OK - Request processed successfully. + ******************************************************************************/ +int ftpx_ProcessRequest( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply*/ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Static variables. + */ + static int nXmitMode = FTP_BINARY_MODE; + static UCHAR *pszRemoteFile = NULL; + static UCHAR *pszRemotePath = NULL; + static UCHAR szXmitFile[MAX_FTP_FILENAME] = ""; + static FILE *fXmitFile = NULL; + + /* Local variables. + */ + int nLastBlock; + int nRcvMode; + int nReturn = SDD_OK; + UCHAR *szSrcFile; + UCHAR *szDstFile; + UCHAR *szPath; + UCHAR szRcvFile[MAX_FTP_FILENAME] = ""; + UCHAR *szFunc = "ftpx_ProcessRequest"; + FILE *fpRcvFile; + + /* If the request block doesnt contain any data, something went wrong + * somewhere? + */ + if(nDataLen <= 1) + { + sprintf(szErrMsg, + "%s: Illegal request, has size of %dBytes", + SDD_EMSG_BADREQ, nDataLen); + return(SDD_FAIL); + } + + /* First byte of the request data block indicates actions required, so + * decipher it. + */ + switch(snzDataBuf[0]) + { + case SDD_FTPX_XMIT: + /* If this is the first time, then create a temporary file and + * process any options. + */ + if(fXmitFile == NULL) + { + /* Create a filename using a constant with the current pid. + */ + sprintf(szXmitFile, "/tmp/XmitFile.%d", getpid()); + + /* Try to open the file. + */ + if((fXmitFile=fopen(szXmitFile, "w")) == NULL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: Failed to create a temporary storage file", + SDD_EMSG_FILEERROR); + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Get the path of the file to on the remote. + */ + if(_FTPX_GetStrArg(&snzDataBuf[1], (nDataLen-1), FTP_SRCPATH, + &szPath) == SDD_FAIL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: Illegal or no Path given in command", + SDD_EMSG_BADPATH); + + /* Close file, no longer needed. + */ + fclose(fXmitFile); + fXmitFile = NULL; + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Get the name of the file to be created on the remote. + */ + if(_FTPX_GetStrArg(&snzDataBuf[1], (nDataLen-1), FTP_SRCFILE, + &szSrcFile) == SDD_FAIL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: Illegal or no Filename given in command", + SDD_EMSG_BADFILE); + + /* Close file, no longer needed. + */ + fclose(fXmitFile); + fXmitFile = NULL; + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Allocate memory to store the Path and File until they + * are needed. + */ + if((pszRemotePath=(UCHAR *)malloc(strlen(szPath)+1)) == NULL || + (pszRemoteFile=(UCHAR *)malloc(strlen(szSrcFile)+1)) == NULL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: No more memory whilst allocating storage", + SDD_EMSG_MEMORY); + + /* Close file, no longer needed. + */ + fclose(fXmitFile); + fXmitFile = NULL; + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Copy temporary contents into the permanent memory we've + * just allocated. + */ + strcpy(pszRemotePath, szPath); + strcpy(pszRemoteFile, szSrcFile); + + /* Get the mode of transfer, if specified, else default to + * binary. + */ + nXmitMode = _FTPX_GetMode(&snzDataBuf[1], (nDataLen-1)); + } + + /* Extract the data which has been passed in the block and store + * in the temporary file opened earlier. + */ + if(_FTPX_GetWriteData(&snzDataBuf[1], (nDataLen-1), fXmitFile, + &nLastBlock, szErrMsg) == SDD_FAIL) + { + /* Tidy up as we are not coming back!!! + */ + fclose(fXmitFile); + fXmitFile = NULL; + free(pszRemotePath); + free(pszRemoteFile); + pszRemotePath = NULL; + pszRemoteFile = NULL; + + /* Delete temporary file as we dont want thousands of these + * populating the temporary directory. + */ + unlink(szXmitFile); + + /* Exit directly as we have nothing left to tidy up. + */ + return(SDD_FAIL); + } + + /* If we wrote the last block successfully, then prepare for FTP + * takeoff. + */ + if(nLastBlock == TRUE) + { + /* Firstly, close the opened file and reset the pointer for + * next time. + */ + fclose(fXmitFile); + fXmitFile = NULL; + + /* Fire the data into hyper space... we assume the FTP link + * is still open, it will fail otherwise. + */ + nReturn=_FTPX_FTPXmitFile(szXmitFile, pszRemotePath, + pszRemoteFile, nXmitMode, szErrMsg); + + /* Free the permanent path and file store, no longer needed. + */ + free(pszRemotePath); + free(pszRemoteFile); + pszRemotePath = NULL; + pszRemoteFile = NULL; + + /* Delete temporary file as we dont want thousands of these + * populating the temporary directory. + */ + unlink(szXmitFile); + } + break; + + case SDD_FTPX_RCV: + /* Get the path of the file to be received. + */ + if(_FTPX_GetStrArg(&snzDataBuf[1], (nDataLen-1), FTP_SRCPATH, + &szPath) == SDD_FAIL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: Illegal or no Path given in command", + SDD_EMSG_BADPATH); + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Get the name of the file to be received. + */ + if(_FTPX_GetStrArg(&snzDataBuf[1], (nDataLen-1), FTP_SRCFILE, + &szSrcFile) == SDD_FAIL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: Illegal or no Filename given in command", + SDD_EMSG_BADFILE); + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Get the mode of transfer, if specified, else default to + * binary. + */ + nRcvMode = _FTPX_GetMode(&snzDataBuf[1], (nDataLen-1)); + + /* Create the name of the temporary file for holding FTP'd data. + */ + sprintf(szRcvFile, "/tmp/RcvFile.%d", getpid()); + + /* Get file from remote site.. lets hope it hasnt closed down, + * maybe the client is the ?A.dasd?. + */ + if(_FTPX_FTPRcvFile(szRcvFile, szPath, szSrcFile, nRcvMode, + szErrMsg) == SDD_FAIL) + { + /* Delete temporary file as we dont want thousands of these + * populating the temporary directory. + */ + unlink(szRcvFile); + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Open the file just populated from the FTP receive ready for + * transmission back to the host. + */ + if((fpRcvFile=fopen(szRcvFile, "r")) == NULL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: Illegal or no Filename given in command", + SDD_EMSG_BADFILE); + + /* Delete temporary file as we dont want thousands of these + * populating the temporary directory. + */ + unlink(szRcvFile); + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Transmit data back to caller...... + */ + nReturn=_FTPX_PutReadData(fpRcvFile, fSendDataCB, szErrMsg); + + /* Close file and remove it... + */ + fclose(fpRcvFile); + unlink(szRcvFile); + break; + + case SDD_FTPX_REN: + /* Get the path where the file to be renamed exists. + */ + if(_FTPX_GetStrArg(&snzDataBuf[1], (nDataLen-1), FTP_SRCPATH, + &szPath) == SDD_FAIL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, "%s: Illegal or no Path given in command", + SDD_EMSG_BADPATH); + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Get the source file name. + */ + if(_FTPX_GetStrArg(&snzDataBuf[1], (nDataLen-1), FTP_SRCFILE, + &szSrcFile) == SDD_FAIL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: Illegal or no source file given in command", + SDD_EMSG_BADPATH); + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Get the destination file name. + */ + if(_FTPX_GetStrArg(&snzDataBuf[1], (nDataLen-1), FTP_DSTFILE, + &szDstFile) == SDD_FAIL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: Illegal or no source file given in command", + SDD_EMSG_BADPATH); + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Call the FTP function to perform the rename. + */ + if(_FTPX_FTPRenFile(szPath,szSrcFile,szDstFile,szErrMsg)==SDD_FAIL) + { + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + break; + + default: + sprintf(szErrMsg, + "%s: Illegal command in request buffer (%x)", + SDD_EMSG_BADREQ, snzDataBuf[0]); + return(SDD_FAIL); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: ftpx_ProcessOOB + * Description: Entry point into driver to process an out of band command + * that may or may not be relevant to current state of + * operation. The task of this function is to decipher the + * command and act on it immediately, ie. a cancel command + * would abort any ProcessRequest that is in process and + * clean up. + * + * Returns: No returns. + ******************************************************************************/ +void ftpx_ProcessOOB( UCHAR nCommand ) /* I: OOB Command */ +{ + /* Local variables. + */ + + /* Decipher command and perform actions accordingly. + */ + switch(nCommand) + { + /* Request to abort current ProcessRequest command and return daemon + * to a waiting-for-request state. + */ + case SDD_ABORT: + break; + + /* Request to close down and exit. + */ + case SDD_EXIT: + break; + + default: + break; + } + + /* Return to caller. + */ + return; +} diff --git a/SDD/sdd_ftpx.h b/SDD/sdd_ftpx.h new file mode 100755 index 0000000..06f75d9 --- /dev/null +++ b/SDD/sdd_ftpx.h @@ -0,0 +1,126 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_ftpx.h + * Description: Server Data-source Driver library driver header file for + * the system command execution driver. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef SDD_FTPX_H +#define SDD_FTPX_H + +/* Define FTP response codes require by this module. +*/ +#define FTP_RESPONSE_NONE 0 +#define FTP_RESPONSE_DATASTART 150 +#define FTP_RESPONSE_CWDOK 200 +#define FTP_RESPONSE_CONNECTED 220 +#define FTP_RESPONSE_GOODBYE 221 +#define FTP_RESPONSE_DATAEND 226 +#define FTP_RESPONSE_PASSIVE 227 +#define FTP_RESPONSE_PWDOK 230 +#define FTP_RESPONSE_CMDOK 250 +#define FTP_RESPONSE_USEROK 331 +#define FTP_RESPONSE_RENFROK 350 +#define FTP_RESPONSE_BADSEQ 503 +#define FTP_RESPONSE_PWDFAIL 530 +#define FTP_RESPONSE_CMDFAIL 550 +#define FTP_REQUIRE_NOTHING 0 +#define FTP_REQUIRE_RESPONSE 1 + +/* Definitions for maxims etc. +*/ +#define MAX_RETURN_BUF 2048+1 +#define MAX_FTP_FILENAME 256 +#define MAX_FTP_BUF_SIZE 65536 +#define MAX_TMP_BUF_SIZE 1024 + +/* Definitions for constants, configurable options etc. +*/ +#define FTP_DATA "DATA=" +#define FTP_END "END=" +#define FTP_SRCFILE "SRCFILE=" +#define FTP_DSTFILE "DSTFILE=" +#define FTP_MODE "MODE=" +#define FTP_SRCPATH "SRCPATH=" +#define FTP_DSTPATH "DSTPATH=" +#define FTP_ASCII_MODE 0 +#define FTP_BINARY_MODE 1 +#define FTP_ASCII "ASCII" +#define FTP_BINARY "BINARY" +#define FTP_CONNECT_TIME 10000 +#define DEF_FTP_SERVICE_NAME "ftp" +#define DEF_FTP_BUF_SIZE 65536 +#define DEF_FTP_BUF_INCSIZE 65536 +#define DEF_FTP_XMIT_SIZE 1024 +#define DEF_SRV_START_PORT 10000 +#define DEF_SRV_END_PORT 15000 + +/* Structure for variables internal to this driver module. +*/ +typedef struct { + UINT nPIChanId; /* Comms identifier for PI channel */ + UINT nPIDataBufLen; /* Max len of snzPIDataBuf */ + UINT nPIDataBufPos; /* Position locator in snzPIDataBuf */ + UINT nPIResponseCode; /* Response code on the PI channel */ + UCHAR *snzPIDataBuf; /* Buffer for incoming confirmations */ + UINT nDTPChanId; /* Comms identifier for DTP channel */ + UINT nDTPConnected; /* DTP channel connected, TRUE=connected */ + ULNG lDTPIPaddr; /* IP address of the DTP server */ + UINT nDTPPortNo; /* Port of the DTP server */ + FILE *fDataFile; /* Data I/O stream for FTP file */ +} FTPX_DRIVER; + +/* Allocate any global variables needed within this module. +*/ +static FTPX_DRIVER FTPX; + +/* Prototypes of internal functions, not seen by any outside module. +*/ +int _FTPX_GetStrArg( UCHAR *, int, UCHAR *, UCHAR ** ); +int _FTPX_GetMode( UCHAR *, int ); +int _FTPX_GetWriteData( UCHAR *, int, FILE *, int *, UCHAR * ); +int _FTPX_PutReadData( FILE *, int (*)(UCHAR *, UINT), UCHAR * ); +void _FTPX_PIDataCB( UINT, UCHAR *, UINT ); +void _FTPX_PICtrlCB( int, ... ); +void _FTPX_DTPDataCB( UINT, UCHAR *, UINT ); +void _FTPX_DTPCtrlCB( int, ... ); +int _FTPX_PISendCmd( UCHAR *, UINT *, UCHAR * ); +int _FTPX_PIGetDTPResponse( void ); +int _FTPX_PISendDTPCmd( UCHAR *, UINT *, UCHAR * ); +int _FTPX_SetMode( UINT, UCHAR * ); +int _FTPX_SetCwd( UCHAR *, UCHAR * ); +int _FTPX_FTPInit( UCHAR *, UCHAR *, UCHAR *, UCHAR * ); +int _FTPX_FTPClose( UCHAR * ); +int _FTPX_FTPRenFile( UCHAR *, UCHAR *, UCHAR *, UCHAR * ); +int _FTPX_FTPRcvFile( UCHAR *, UCHAR *, UCHAR *, UINT, UCHAR * ); +int _FTPX_FTPXmitFile( UCHAR *, UCHAR *, UCHAR *, UINT, UCHAR * ); + +#endif /* SDD_FTPX_H */ diff --git a/SDD/sdd_java.c b/SDD/sdd_java.c new file mode 100755 index 0000000..86af2e5 --- /dev/null +++ b/SDD/sdd_java.c @@ -0,0 +1,199 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_java.c + * Description: Server Data-source Driver library driver to handle java + * connectivity. + * + * 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 + +/* Indicate that we are a C module for any header specifics. +*/ +#define SDD_JAVA_C + +/* Bring in local specific header files. +*/ +#include "sdd.h" +#include "sdd_java.h" + +/****************************************************************************** + * Function: java_InitService + * Description: Entry point which initialises the driver into a defined state. + * It is mandatory that this function is called before any other + * in order for the driver to function correctly. The caller + * provides it with two types of data, 1) A structure containing + * data for it to use in initialising itself, 2) a pointer to a + * buffer which the driver uses to place an error message should + * it not be able to complete initialisation. + * + * Returns: SDD_FAIL- An error occurred in initialising the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver initialised successfully. + ******************************************************************************/ +int java_InitService( SERVICEDETAILS *sServiceDet, /* I: Init data */ + UCHAR *szErrStr ) /* O: Error message */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "java_InitService"; + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "Java Driver: Initialised:"); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: java_CloseService + * Description: Entry point which performs a drive closedown. The closedown + * procedure ensure that the driver returns to a virgin state + * (ie.like at power up) so that InitService can be called again. + * + * Returns: SDD_FAIL- An error occurred in closing the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver successfully closed. + ******************************************************************************/ +int java_CloseService( UCHAR *szErrMsg ) /* O: Error message if failed */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "java_CloseService"; + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "Java Driver: Closed."); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: java_ProcessRequest + * Description: Entry point into driver to initiate the driver into + * processing a request. A data block is passed as a parameter + * to the driver which represents a request with relevant + * parameters. The data within the structure is only relevant + * to the original client and this driver code. + * + * Returns: SDD_FAIL- An error occurred within the driver whilst trying to + * process the request, see error text. + * SDD_OK - Request processed successfully. + ******************************************************************************/ +int java_ProcessRequest( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply*/ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "java_ProcessRequest"; + + /* If the request block doesnt contain any data, something went wrong + * somewhere?? + */ + if(nDataLen <= 1) + { + sprintf(szErrMsg, + "%s: Illegal request, has size of %dBytes", + SDD_EMSG_BADREQ, nDataLen); + return(SDD_FAIL); + } + + /* First byte of the request data block indicates actions required, so + * decipher it. + */ + switch(snzDataBuf[0]) + { + default: + sprintf(szErrMsg, + "%s: Illegal command in request buffer (%x)", + SDD_EMSG_BADCMD, snzDataBuf[0]); + return(SDD_FAIL); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: java_ProcessOOB + * Description: Entry point into driver to process an out of band command + * that may or may not be relevant to current state of + * operation. The task of this function is to decipher the + * command and act on it immediately, ie. a cancel command + * would abort any ProcessRequest that is in process and + * clean up. + * + * Returns: No returns. + ******************************************************************************/ +void java_ProcessOOB( UCHAR nCommand ) /* I: OOB Command */ +{ + /* Local variables. + */ + + /* Decipher command and perform actions accordingly. + */ + switch(nCommand) + { + /* Request to abort current ProcessRequest command and return daemon + * to a waiting-for-request state. + */ + case SDD_ABORT: + break; + + /* Request to close down and exit. + */ + case SDD_EXIT: + break; + + default: + break; + } + + + /* Return to caller. + */ + return; +} diff --git a/SDD/sdd_java.h b/SDD/sdd_java.h new file mode 100755 index 0000000..e96461d --- /dev/null +++ b/SDD/sdd_java.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_java.h + * Description: Server Data-source Driver library driver header file for + * java driver. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef SDD_JAVA_H +#define SDD_JAVA_H + +/* Definitions for maxims etc. +*/ + +#endif /* SDD_JAVA_H */ diff --git a/SDD/sdd_odbc.c b/SDD/sdd_odbc.c new file mode 100755 index 0000000..817910f --- /dev/null +++ b/SDD/sdd_odbc.c @@ -0,0 +1,1671 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_odbc.c + * Description: Server Data-source Driver library driver to handle odbc + * connectivity. + * + * 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 + +/* Indicate that we are a C module for any header specifics. +*/ +#define SDD_ODBC_C + +/* Bring in local specific header files. +*/ +#include "sdd.h" + +/* Bring in required ODBC header files. +*/ +#include +/* #include */ +#include +#include "sdd_odbc.h" + +/****************************************************************************** + * Function: _ODBC_GetArg + * Description: Function to scan an input buffer and extract a required + * argument from it. + * + * Returns: SDD_FAIL- Couldnt obtain argument. + * SDD_OK - Argument obtained and validated. + ******************************************************************************/ +int _ODBC_GetArg( UCHAR *szArgType, /* I: Type of Arg to scan for */ + UCHAR *snzDataBuf, /* I: Input buffer */ + int nDataLen, /* I: Len of data */ + UCHAR **pszArg ) /* O: Pointer to Arg */ +{ + /* Local variables. + */ + int nCmpLen; + int nPos; + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_ODBC_GetArg"; + + /* Go through the input buffer looking for a 'szArgType' within it. If it + * exists, then note the position just after it as this contains the + * database name. + */ + for(nPos=0, nCmpLen=strlen(szArgType); nPos < nDataLen; nPos++) + { + /* If a match occurs, then setup the pointer to correct location. + */ + if(strncmp(&snzDataBuf[nPos], szArgType, nCmpLen) == 0) + { + /* Make sure that the match is not a sub-match as some of the + * variables we are scanning for are similar. + */ + if( (nPos == 0) || + (nPos > 0 && snzDataBuf[nPos-1] == '\0') || + (nPos > 0 && isspace(snzDataBuf[nPos-1])) ) + { + nPos += nCmpLen; + break; + } + } + } + + /* If the pointer did not reach the end of the buffer then we have + * located a valid name. + */ + if(nPos < nDataLen) + { + /* Setup the callers pointer to point to the correct location + * in the buffer. + */ + *pszArg = &snzDataBuf[nPos]; + nReturn=SDD_OK; + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _ODBC_LogODBCError + * Description: Function to dump an error message/code from the ODBC driver + * to the log device. Typically used for debugging. + * + * Returns: No returns. + ******************************************************************************/ +void _ODBC_LogODBCError( HSTMT hStmt ) +{ + /* Local variables. + */ + SDWORD lNativeError; + SWORD nActualMsgLen; + UCHAR szErrMsg[MAX_TMPBUFLEN]; + UCHAR szSqlState[10]; + UCHAR *szFunc = "_ODBC_LogODBCError"; + + /* Call ODBC underlying to extract the error message/code. + */ + SQLError(ODBC.hEnv, ODBC.hDbc, hStmt, szSqlState, + &lNativeError, szErrMsg, SQL_MAX_MESSAGE_LENGTH -1, + &nActualMsgLen); + + /* Log to logger. + */ + Lgr(LOG_ALERT, szFunc, "ODBC Error: Msg=%s, State=%s, NativeError=%ld", + szErrMsg, szSqlState, lNativeError); + + /* All done.. + */ + return; +} + +/****************************************************************************** + * Function: _ODBC_RunSql + * Description: Function to execute a given buffer of SQL on the current + * database and return resultant data to the original caller. + * + * Returns: SDD_FAIL- SQL execution failed, see error message. + * SDD_OK - SQL execution succeeded. + ******************************************************************************/ +int _ODBC_RunSql( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + UINT nFirstRow; + UINT nOffSet; + UINT nRowBufLen; + UINT nRowBufPos; + int nReturn = SDD_OK; + RETCODE nResult; + SWORD nBufLen; + SWORD nColScale; + SWORD nColType; + SWORD nNdx; + SWORD nNumCols; + SWORD nNullType; + SDWORD nMaxColLen; + SDWORD nValType; + SDWORD *panColLen = NULL; + UDWORD nColPrecision; + UCHAR *pszSqlBuf = NULL; + UCHAR *pszRowBuf = NULL; + UCHAR *pacRowCol = NULL; + UCHAR *szFunc = "_ODBC_RunSql"; + HSTMT hStmt; + + /* Allocate memory for incoming statement storage within odbc. + */ + nResult=SQLAllocStmt(ODBC.hDbc, &hStmt); + if( nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt allocate SQL statement memory", + SDD_EMSG_MEMORY); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* Allocate memory to null terminate inbound SQL prior to preparing it. + */ + if((pszSqlBuf=(UCHAR *)malloc(nDataLen+1)) == NULL) + { + /* Build exit message to let caller know why we failed. + */ + sprintf(szErrMsg, "%s: Out of memory allocating SQL buffer", + SDD_EMSG_MEMORY); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* Copy original SQL into buffer and terminate it. + */ + memcpy(pszSqlBuf, snzDataBuf, nDataLen); + pszSqlBuf[nDataLen] = '\0'; + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "SQL: %s", pszSqlBuf); + + /* Load the SQL into the odbc drivers memory. + */ + nResult=SQLPrepare(hStmt, pszSqlBuf, (SDWORD)nDataLen); + + /* Free up memory, no need to hang on to it.. + */ + free(pszSqlBuf); + pszSqlBuf = NULL; + + /* Check result code to see if prepare created errors. + */ + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: SQL syntax error reported by ODBC driver", + SDD_EMSG_SQLLOAD); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* Get the number of result columns that will be returned. + */ + nResult=SQLNumResultCols(hStmt, &nNumCols); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt determine number of result columns", + SDD_EMSG_SQLSYNTAX); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* Allocate memory to bind the returning column lengths on. + */ + if((panColLen=(SDWORD *)malloc(nNumCols * sizeof(SDWORD))) == NULL) + { + /* Build exit message to let caller know why we failed. + */ + sprintf(szErrMsg, "%s: Out of memory allocating column lengths", + SDD_EMSG_MEMORY); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* Work out the lengths of each column. All data will be returned as + * ASCII characters, so work with this basis. Within the same loop, work + * out the maximum column name length to be used for buffer allocation. + */ + for(nNdx=0, nMaxColLen=0; nNdx < nNumCols; nNdx++) + { + /* Request the ODBC driver to return the length of the column. + */ + nResult=SQLColAttributes(hStmt, nNdx+1, SQL_COLUMN_DISPLAY_SIZE, + NULL, 0, NULL, panColLen+nNdx); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt determine length of a result column", + SDD_EMSG_SQLSYNTAX); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* Is this column length greater than our current maximum? + */ + if(*(panColLen+nNdx) > nMaxColLen) + nMaxColLen = *(panColLen+nNdx) + 1; + } + + /* Allocate memory to bind the returning columns on. + */ + if((pacRowCol=(UCHAR *)malloc(nNumCols*nMaxColLen)) == NULL) + { + /* Build exit message to let caller know why we failed. + */ + sprintf(szErrMsg, "%s: Out of memory allocating column bindings", + SDD_EMSG_MEMORY); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* Bind the expected columns to the allocated memory. + */ + for(nNdx=0, nOffSet=0, nValType=SQL_NO_TOTAL; nNdx < nNumCols; + nNdx++, nOffSet+=nMaxColLen) + { + /* Bind column according to index value. + */ + nResult=SQLBindCol(hStmt, nNdx+1, SQL_C_CHAR, pacRowCol+nOffSet, + nMaxColLen, &nValType); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt bind a column to memory", + SDD_EMSG_SQLRUNERR); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + } + + /* Allocate memory to hold all column data as one continous character + * row, seperated by the pipe symbol. + */ + nRowBufLen=nMaxColLen * nNumCols; + if((pszRowBuf=(UCHAR *)malloc(nRowBufLen+1)) == NULL) + { + /* Build exit message to let caller know why we failed. + */ + sprintf(szErrMsg, "%s: Out of memory allocating row return buffer", + SDD_EMSG_MEMORY); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* OK, lets go for it, execute SQL time. + */ + nResult=SQLExecute(hStmt); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Runtime error during SQL execution", + SDD_EMSG_SQLRUNERR); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* If there is no data to be fetched, then just fast forward. + */ + if(nNumCols == 0) + { + goto _ODBC_RunSql_Exit; + } + + /* Initialise any required variables. + */ + ODBC.nAbortPending = FALSE; + nFirstRow = TRUE; + + /* Loop until we have exhausted the number of rows or an ABORT condition + * is generated. + */ + do { + /* If this is the first row then get the column header, else get the + * actual rows. + */ + if( nFirstRow == TRUE ) + { + /* To get the names of each column header, go in a loop and load + * the name into the row array buffers. + */ + for(nNdx=0, nOffSet=0; nNdx < nNumCols; nNdx++, nOffSet+=nMaxColLen) + { + /* Get the name of one column, equal to nNdx+1. + */ + nResult=SQLDescribeCol(hStmt, nNdx+1, + (UCHAR *)pacRowCol+nOffSet, + nMaxColLen, &nBufLen, &nColType, + &nColPrecision, &nColScale, + &nNullType); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt get name of a column", + SDD_EMSG_SQLSYNTAX); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + } + + /* Toggle flag to ensure that we dont send the header again. + */ + nFirstRow = FALSE; + } else + { + /* Fetch a row of results from the ODBC driver. + */ + nResult=SQLFetch(hStmt); + + /* If we dont have a success status or a no more data status, + * then exit, as we hit an error that we dont intend to handle. + */ + if(nResult != SQL_SUCCESS && nResult != SQL_NO_DATA_FOUND && + nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Runtime error whilst fetching data", + SDD_EMSG_SQLRUNERR); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + + /* If there are any error messages (or info) associated with + * this operation then log it. + */ + if(nResult == SQL_SUCCESS_WITH_INFO) + { + _ODBC_LogODBCError( hStmt ); + } + } + + /* If there is any data to be transmitted, ie. nResult is set to + * SQL_SUCCESS or SQL_SUCCESS_WITH_INFO then transmit it to the + * client. + */ + if(nResult == SQL_SUCCESS || nResult == SQL_SUCCESS_WITH_INFO) + { + /* Clear out the row buffer prior to assembling the array buffers + * into it. + */ + memset(pszRowBuf, ' ', nRowBufLen); + pszRowBuf[nRowBufLen] = '\0'; + + /* Place the data in each array buffer into the row buffer with + * appropriate seperation characters. + */ + for(nNdx=0, nOffSet=0, nRowBufPos=0; nNdx < nNumCols; + nNdx++, nOffSet+=nMaxColLen) + { + /* Copy actual data into the row buffer. + */ + memcpy(&pszRowBuf[nRowBufPos], pacRowCol+nOffSet, + strlen((UCHAR *)pacRowCol+nOffSet)); + + /* Update row position. + */ + if(*(panColLen+nNdx) > strlen((UCHAR *)pacRowCol+nOffSet)) + { + nRowBufPos += *(panColLen+nNdx); + } else + { + nRowBufPos += strlen((UCHAR *)pacRowCol+nOffSet); + } + + /* Insert seperation character if this is not the last col. + */ + if( nNdx < nNumCols-1 ) + { + strncpy(&pszRowBuf[nRowBufPos++], DEF_COLSEP, + strlen(DEF_COLSEP)); + } + } + + /* Terminate string prior to transmission and set correct length. + */ + pszRowBuf[nRowBufPos] = '\0'; + + /* Log row if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, "%s", pszRowBuf); + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(pszRowBuf, nRowBufPos) == SDD_FAIL) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_RunSql_Exit; + } + } + } while( (ODBC.nAbortPending == FALSE) && + (nResult == SQL_SUCCESS || nResult == SQL_SUCCESS_WITH_INFO) ); + + /* If an abort command arrived halfway through processing then tidy up + * for exit. + */ + if(ODBC.nAbortPending == TRUE) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: ABORT received, query abandoned", + SDD_EMSG_ABORTRCVD); + nReturn = SDD_FAIL; + } + +_ODBC_RunSql_Exit: + /* Free memory prior to exit. + */ + if(panColLen != NULL) + free(panColLen); + if(pacRowCol != NULL) + free(pacRowCol); + if(pszRowBuf != NULL) + free(pszRowBuf); + + /* Clear SQL Cursor prior to exit. + */ + nResult=SQLFreeStmt(hStmt, SQL_CLOSE); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Failed to close SQL cursor", + SDD_EMSG_SQLRUNERR); + + /* Set up failure code to indicate condition. + */ + nReturn = SDD_FAIL; + } + + /* Free up statement memory. + */ + nResult=SQLFreeStmt(hStmt, SQL_DROP); + if( nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Failed to free ODBC handle", + SDD_EMSG_SQLRUNERR); + + /* Setup failure code to indicate condition. + */ + nReturn = SDD_FAIL; + } + + /* Return success/fail to caller depending on execution results. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _ODBC_ListDB + * Description: Function to list all the names of databases available on the + * currently open data source. + * + * Returns: SDD_FAIL- SQL execution failed, see error message. + * SDD_OK - SQL execution succeeded. + ******************************************************************************/ +int _ODBC_ListDB( int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + UINT nFirstRow; + int nReturn = SDD_OK; + RETCODE nResult; + UWORD nDirection; + SWORD nDescrBufLen; + SWORD nDSNBufLen; + UCHAR szDataSourceName[SQL_MAX_DSN_LENGTH+1]; + UCHAR szDescription[MAX_DSN_DESCR_LEN+1]; + UCHAR szRowBuf[SQL_MAX_DSN_LENGTH + MAX_DSN_DESCR_LEN + 5]; + UCHAR *szFunc = "_ODBC_ListDB"; + + /* Initialise any required variables. + */ + ODBC.nAbortPending = FALSE; + nFirstRow = TRUE; + + /* Go in a loop, reading each database name and returning it to the caller + * until all have been exhausted. + */ + do { + /* If this is the first row then start the query at the beginning. + */ + nDirection=(nFirstRow == TRUE ? SQL_FETCH_FIRST : SQL_FETCH_NEXT); + nFirstRow = FALSE; + + /* Query the driver for the next database name. + */ + nResult=SQLDataSources(ODBC.hEnv, nDirection, szDataSourceName, + SQL_MAX_DSN_LENGTH, &nDSNBufLen, + szDescription, MAX_DSN_DESCR_LEN, + &nDescrBufLen); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO && + nResult != SQL_NO_DATA_FOUND) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( 0 ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt get data source name", + SDD_EMSG_BADDBASE); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListDB_Exit; + } + + /* If data is available, return it to calling client. + */ + if(nResult == SQL_SUCCESS || nResult == SQL_SUCCESS_WITH_INFO) + { + /* Build up the row for return from components. + */ + sprintf(szRowBuf, "%s", szDataSourceName); + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(szRowBuf, strlen(szRowBuf)) == SDD_FAIL) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListDB_Exit; + } + } + } while( (ODBC.nAbortPending == FALSE) && (nResult != SQL_NO_DATA_FOUND) ); + + /* If an abort command arrived halfway through processing then tidy up + * for exit. + */ + if(ODBC.nAbortPending == TRUE) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: ABORT received, query abandoned", + SDD_EMSG_ABORTRCVD); + nReturn = SDD_FAIL; + } + + /* Complete processing by freeing/closing used resources and exitting + * with pre-set return code. + */ +_ODBC_ListDB_Exit: + + /* Return success/fail to caller depending on execution results. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _ODBC_ListTables + * Description: Function to list all names of tables in a given database + * (or current database if no database name given). + * + * Returns: SDD_FAIL- SQL execution failed, see error message. + * SDD_OK - SQL execution succeeded. + ******************************************************************************/ +int _ODBC_ListTables( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + RETCODE nResult; + SDWORD nOwnerLen; + SDWORD nQualifierLen; + SDWORD nRemarksLen; + SDWORD nTableLen; + SDWORD nTypeLen; + UCHAR *pszRowBuf = NULL; + UCHAR szOwner[MAX_TABLE_DESCR_LEN]; + UCHAR szQualifier[MAX_TABLE_DESCR_LEN]; + UCHAR *pszDbName; + UCHAR szRemarks[MAX_TABLE_DESCR_LEN]; + UCHAR szTableName[MAX_TABLE_DESCR_LEN]; + UCHAR szTableType[MAX_TABLE_DESCR_LEN]; + UCHAR *szFunc = "_ODBC_ListTables"; + HSTMT hStmt; + + /* See if the caller has provided the name of a new database. If HE hasnt + * then assume current database. + */ + if(_ODBC_GetArg(ODBC_DBNAME, snzDataBuf, nDataLen, &pszDbName) == SDD_FAIL) + { + /* If the length of the input buffer indicates that there is data within + * it, yet it does not contain a database name, then it is illegal. + */ + if(nDataLen > 1) + { + sprintf(szErrMsg, "%s: Invalid DATABASE NAME provided", + SDD_EMSG_BADCMD); + return(SDD_FAIL); + } else + { + /* Setup the variable so that the select below will work on the + * current database. + */ + pszDbName = NULL; + } + } else + { + /* AAAAAHHHHH! Database name switching not yet supported... tee + * hee, message for the sucker!! + */ + sprintf(szErrMsg, + "%s: ODBC switch to database function not yet implemented", + SDD_EMSG_NOTYI); + return(SDD_FAIL); + } + + /* Allocate memory for the statement handle. + */ + nResult=SQLAllocStmt(ODBC.hDbc, &hStmt); + if( nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt allocate Statement memory", + SDD_EMSG_MEMORY); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListTables_Exit; + } + + /* Call odbc function to generate a listing of all the tables in the + * data source. + */ + nResult=SQLTables(hStmt, NULL, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS, + NULL,SQL_NTS); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt request table details (%d)", + SDD_EMSG_SQLSYNTAX, nResult); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListTables_Exit; + } + + /* Bind the expected columns to the allocated memory. + */ + SQLBindCol(hStmt, 1, SQL_C_CHAR, szQualifier, MAX_TABLE_DESCR_LEN, + &nQualifierLen); + SQLBindCol(hStmt, 2, SQL_C_CHAR, szOwner, MAX_TABLE_DESCR_LEN, + &nOwnerLen); + SQLBindCol(hStmt, 3, SQL_C_CHAR, szTableName, MAX_TABLE_DESCR_LEN, + &nTableLen); + SQLBindCol(hStmt, 4, SQL_C_CHAR, szTableType, MAX_TABLE_DESCR_LEN, + &nTypeLen); + SQLBindCol(hStmt, 5, SQL_C_CHAR, szRemarks, MAX_TABLE_DESCR_LEN, + &nRemarksLen); + + /* Allocate memory to hold all column data as one continous character + * row, seperated by the pipe symbol. + */ + if((pszRowBuf=(UCHAR *)malloc( ((MAX_TABLE_DESCR_LEN+1)*5) +1)) == NULL) + { + /* Build exit message to let caller know why we failed. + */ + sprintf(szErrMsg, "%s: Out of memory allocating row return buffer", + SDD_EMSG_MEMORY); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListTables_Exit; + } + + /* Initialise any required variables. + */ + ODBC.nAbortPending = FALSE; + + /* Loop until we have exhausted the number of rows or an ABORT condition + * is generated. + */ + do { + /* Fetch a row of results from the ODBC driver. + */ + nResult=SQLFetch(hStmt); + + /* If we dont have a success status or a no more data status, + * then exit, as we hit an error that we dont intend to handle. + */ + if(nResult != SQL_SUCCESS && nResult != SQL_NO_DATA_FOUND && + nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Runtime error whilst fetching data", + SDD_EMSG_SQLRUNERR); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListTables_Exit; + } + + /* If there are any error messages (or info) associated with + * this operation then log it. + */ + if(nResult == SQL_SUCCESS_WITH_INFO) + { + _ODBC_LogODBCError( hStmt ); + } + + /* If there is any data to be transmitted, ie. nResult is set to + * SQL_SUCCESS or SQL_SUCCESS_WITH_INFO then transmit it to the + * client. + */ + if(nResult == SQL_SUCCESS || nResult == SQL_SUCCESS_WITH_INFO) + { + /* Place the appropriate data in the return buffer. + */ + sprintf(pszRowBuf, "%s%s%s", + szTableName, DEF_COLSEP, szTableType); + + /* Log row if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, "%s", pszRowBuf); + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(pszRowBuf, strlen(pszRowBuf)) == SDD_FAIL) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListTables_Exit; + } + } + } while( (ODBC.nAbortPending == FALSE) && + (nResult == SQL_SUCCESS || nResult == SQL_SUCCESS_WITH_INFO) ); + + /* If an abort command arrived halfway through processing then tidy up + * for exit. + */ + if(ODBC.nAbortPending == TRUE) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: ABORT received, query abandoned", + SDD_EMSG_ABORTRCVD); + nReturn = SDD_FAIL; + } + +_ODBC_ListTables_Exit: + /* Free memory prior to exit. + */ + if(pszRowBuf != NULL) + free(pszRowBuf); + + /* Clear SQL Cursor prior to exit. + */ + nResult=SQLFreeStmt(hStmt, SQL_CLOSE); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Failed to close SQL cursor", + SDD_EMSG_SQLRUNERR); + + /* Set up failure code to indicate condition. + */ + nReturn = SDD_FAIL; + } + + /* Free up statement memory. + */ + nResult=SQLFreeStmt(hStmt, SQL_DROP); + if( nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Failed to free ODBC handle", + SDD_EMSG_SQLRUNERR); + + /* Setup failure code to indicate condition. + */ + nReturn = SDD_FAIL; + } + + + /* Return success/fail to caller depending on execution results. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _ODBC_ListCols + * Description: Function to list all names and attributes of columns in a + * given table in a given database (or current database/table if + * no database name given). + * + * Returns: SDD_FAIL- SQL execution failed, see error message. + * SDD_OK - SQL execution succeeded. + ******************************************************************************/ +int _ODBC_ListCols( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + UINT nRowCount = 0; + int nReturn = SDD_OK; + RETCODE nResult; + SWORD nDataType; + SWORD nScale; + SWORD nRadix; + SWORD nNullable; + SDWORD lLength; + SDWORD lPrecision; + SDWORD nOwnerLen; + SDWORD nQualifierLen; + SDWORD nRemarksLen; + SDWORD nTableLen; + SDWORD nColLen; + SDWORD nDataTypeLen; + SDWORD nNameTypeLen; + SDWORD nPrecisionLen; + SDWORD nLengthLen; + SDWORD nScaleLen; + SDWORD nRadixLen; + SDWORD nNullableLen; + UCHAR *pszRowBuf = NULL; + UCHAR *pszTableName; + UCHAR *pszDbName; + UCHAR *pszColFilter; + UCHAR szColName[MAX_TABLE_DESCR_LEN]; + UCHAR szQualifier[MAX_TABLE_DESCR_LEN]; + UCHAR szOwner[MAX_TABLE_DESCR_LEN]; + UCHAR szTmpTableName[MAX_TABLE_DESCR_LEN]; + UCHAR szTypeName[MAX_TABLE_DESCR_LEN]; + UCHAR szRemarks[MAX_TABLE_DESCR_LEN]; + UCHAR *szFunc = "_ODBC_ListTables"; + HSTMT hStmt; + + /* See if the caller has provided the name of a new database. If HE hasnt + * then assume current database. + */ + if(_ODBC_GetArg(ODBC_DBNAME, snzDataBuf, nDataLen, &pszDbName) == SDD_FAIL) + { + /* Setup the variable so that the select below will work on the + * current database. + */ + pszDbName = NULL; + } else + { + /* AAAAAHHHHH! Database name switching not yet supported... tee + * hee, message for the sucker!! + */ + sprintf(szErrMsg, + "%s: ODBC switch to database function not yet implemented", + SDD_EMSG_NOTYI); + return(SDD_FAIL); + } + + /* Caller must provide the name of a table... else what do we scan for... + * a French Empire, vapourware... tell me more!! + */ + if(_ODBC_GetArg(ODBC_TABLENAME, snzDataBuf, nDataLen, &pszTableName) + == SDD_FAIL) + { + /* Always generate an error... French Empire.. bah humbug! + */ + sprintf(szErrMsg, "%s: Invalid TABLE NAME provided", + SDD_EMSG_BADARGS); + return(SDD_FAIL); + } + + /* See if the caller has provided the name of a specific column. If HE has + * then he requires data limited to just that one column. + */ + if(_ODBC_GetArg(ODBC_COLFILTER,snzDataBuf,nDataLen,&pszColFilter)==SDD_FAIL) + { + /* Setup the variable so that the column filter logic below wont work + * as no specific column name has been provided, hence the caller + * wants all columns. + */ + pszColFilter = NULL; + } + + /* Allocate memory for the statement handle. + */ + nResult=SQLAllocStmt(ODBC.hDbc, &hStmt); + if( nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt allocate Statement memory", + SDD_EMSG_MEMORY); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListColumns_Exit; + } + + /* Call odbc function to generate a listing of all the columns in a + * given table in the current data source. + */ + nResult=SQLColumns(hStmt, NULL, SQL_NTS, NULL, SQL_NTS, + pszTableName, SQL_NTS, NULL, SQL_NTS); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt request column details (%d)", + SDD_EMSG_SQLSYNTAX, nResult); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListColumns_Exit; + } + + /* Bind the expected columns to the allocated memory. + */ + SQLBindCol(hStmt, 1, SQL_C_CHAR, szQualifier, MAX_TABLE_DESCR_LEN, + &nQualifierLen); + SQLBindCol(hStmt, 2, SQL_C_CHAR, szOwner, MAX_TABLE_DESCR_LEN, + &nOwnerLen); + SQLBindCol(hStmt, 3, SQL_C_CHAR, szTmpTableName, MAX_TABLE_DESCR_LEN, + &nTableLen); + SQLBindCol(hStmt, 4, SQL_C_CHAR, szColName, MAX_TABLE_DESCR_LEN, + &nColLen); + SQLBindCol(hStmt, 5, SQL_C_SSHORT, &nDataType, 0, &nDataTypeLen); + SQLBindCol(hStmt, 6, SQL_C_CHAR, szTypeName, MAX_TABLE_DESCR_LEN, + &nNameTypeLen); + SQLBindCol(hStmt, 7, SQL_C_SLONG, &lPrecision, 0, &nPrecisionLen); + SQLBindCol(hStmt, 8, SQL_C_SLONG, &lLength, 0, &nLengthLen); + SQLBindCol(hStmt, 9, SQL_C_SSHORT, &nScale, 0, &nScaleLen); + SQLBindCol(hStmt, 10, SQL_C_SSHORT, &nRadix, 0, &nRadixLen); + SQLBindCol(hStmt, 11, SQL_C_SSHORT, &nNullable, 0, &nNullableLen); + SQLBindCol(hStmt, 12, SQL_C_CHAR, szRemarks, MAX_TABLE_DESCR_LEN, + &nRemarksLen); + + /* Allocate memory to hold all column data as one continous character + * row, seperated by the pipe symbol. + */ + if((pszRowBuf=(UCHAR *)malloc( ((MAX_TABLE_DESCR_LEN+1)*12) +1)) == NULL) + { + /* Build exit message to let caller know why we failed. + */ + sprintf(szErrMsg, "%s: Out of memory allocating row return buffer", + SDD_EMSG_MEMORY); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListColumns_Exit; + } + + /* Initialise any required variables. + */ + ODBC.nAbortPending = FALSE; + + /* Loop until we have exhausted the number of rows or an ABORT condition + * is generated. + */ + do { + /* Fetch a row of results from the ODBC driver. + */ + nResult=SQLFetch(hStmt); + + /* If we dont have a success status or a no more data status, + * then exit, as we hit an error that we dont intend to handle. + */ + if(nResult != SQL_SUCCESS && nResult != SQL_NO_DATA_FOUND && + nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Runtime error whilst fetching data", + SDD_EMSG_SQLRUNERR); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListColumns_Exit; + } + + /* If there are any error messages (or info) associated with + * this operation then log it. + */ + if(nResult == SQL_SUCCESS_WITH_INFO) + { + _ODBC_LogODBCError( hStmt ); + } + + /* If there is any data to be transmitted, ie. nResult is set to + * SQL_SUCCESS or SQL_SUCCESS_WITH_INFO then run through the column + * filter if applicable and transmit it to the client. + */ + if(nResult == SQL_SUCCESS || nResult == SQL_SUCCESS_WITH_INFO) + { + /* Is the column filter active? If so, only pass the column name + * which has been specified. If it hasnt, pass all columns. + */ + if( (pszColFilter != NULL && strcmp(pszColFilter, szColName) == 0) || + (pszColFilter == NULL) ) + { + /* Place the appropriate data in the return buffer. + */ + sprintf(pszRowBuf, + "%s%s%s%s%ld%s%ld%s%d%s%d%s%d", + szColName, DEF_COLSEP, + szTypeName, DEF_COLSEP, + lPrecision, DEF_COLSEP, + lLength, DEF_COLSEP, + nScale, DEF_COLSEP, + nRadix, DEF_COLSEP, + nNullable); + + /* Log row if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, "%s", pszRowBuf); + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(pszRowBuf, strlen(pszRowBuf)) == SDD_FAIL) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + + /* Goto exit point, setting error code on way out. + */ + nReturn = SDD_FAIL; + goto _ODBC_ListColumns_Exit; + } else + { + /* Increment row counter to indicate number of rows returned. + */ + nRowCount++; + } + } + } + } while( (ODBC.nAbortPending == FALSE) && + (nResult == SQL_SUCCESS || nResult == SQL_SUCCESS_WITH_INFO) ); + + /* If no rows where detected or returned, then there was an error with the + * column, table or database name. + */ + if(nRowCount == 0) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + (pszColFilter == NULL ? "%s: Table name not known by system" : + "%s: Column name not valid for table"), + SDD_EMSG_SQLRUNERR); + + /* Setting error code to indicate condition. + */ + nReturn = SDD_FAIL; + } + + /* If an abort command arrived halfway through processing then tidy up + * for exit. + */ + if(ODBC.nAbortPending == TRUE) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: ABORT received, query abandoned", + SDD_EMSG_ABORTRCVD); + nReturn = SDD_FAIL; + } + +_ODBC_ListColumns_Exit: + /* Free memory prior to exit. + */ + if(pszRowBuf != NULL) + free(pszRowBuf); + + /* Clear SQL Cursor prior to exit. + */ + nResult=SQLFreeStmt(hStmt, SQL_CLOSE); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( hStmt ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Failed to close SQL cursor", + SDD_EMSG_SQLRUNERR); + + /* Set up failure code to indicate condition. + */ + nReturn = SDD_FAIL; + } + + /* Free up statement memory. + */ + nResult=SQLFreeStmt(hStmt, SQL_DROP); + if( nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Failed to free ODBC handle", + SDD_EMSG_SQLRUNERR); + + /* Setup failure code to indicate condition. + */ + nReturn = SDD_FAIL; + } + + /* Return success/fail to caller depending on execution results. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: odbc_InitService + * Description: Entry point which initialises the driver into a defined state. + * It is mandatory that this function is called before any other + * in order for the driver to function correctly. The caller + * provides it with two types of data, 1) A structure containing + * data for it to use in initialising itself, 2) a pointer to a + * buffer which the driver uses to place an error message should + * it not be able to complete initialisation. + * + * Returns: SDD_FAIL- An error occurred in initialising the driver and an + * error message is stored in szErrMsg. + * SDD_OK - Driver initialised successfully. + ******************************************************************************/ +int odbc_InitService( SERVICEDETAILS *sServiceDet, /* I: Init data */ + UCHAR *szErrMsg ) /* O: Error message */ +{ + /* Local variables. + */ + RETCODE nResult; + UCHAR *szFunc = "odbc_InitService"; + + /* Copy all configuration data out of the service structure. + */ + strcpy(ODBC.szUserName, sServiceDet->uServiceInfo.sODBCInfo.szUser); + strcpy(ODBC.szPassword, sServiceDet->uServiceInfo.sODBCInfo.szPassword); + strcpy(ODBC.szServer, sServiceDet->uServiceInfo.sODBCInfo.szServer); + strcpy(ODBC.szDatabase, + sServiceDet->uServiceInfo.sODBCInfo.szDatabase); + + /* Initialise memory etc within the ODBC API. + */ + nResult=SQLAllocEnv(&ODBC.hEnv); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( (HSTMT)0 ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt initialise ODBC API env memory", + SDD_EMSG_MEMORY); + return(SDD_FAIL); + } + + /* If there is information with previos command, display it during + * debug mode. + */ + if(nResult == SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( (HSTMT)0 ); + } + + /* Initialise and allocate ODBC API connection memory. + */ + nResult=SQLAllocConnect( ODBC.hEnv, &ODBC.hDbc ); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( (HSTMT)0 ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt initialise ODBC API connection memory", + SDD_EMSG_MEMORY); + return(SDD_FAIL); + } + + /* If there is information with previous command, display it during + * debug mode. + */ + if(nResult == SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( (HSTMT)0 ); + } + + /* Create a connection to the data source. + */ + nResult=SQLConnect(ODBC.hDbc, ODBC.szServer, SQL_NTS, + ODBC.szUserName, SQL_NTS, + ODBC.szPassword, SQL_NTS ); + if(nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( (HSTMT)0 ); + + /* Build up exit message and get out. + */ + sprintf(szErrMsg, "%s: Couldnt connect to data source", + SDD_EMSG_BADLOGIN); + return(SDD_FAIL); + } + + /* If there is information with previos command, display it during + * debug mode. + */ + if(nResult == SQL_SUCCESS_WITH_INFO) + { + /* Log message to indicate reason. + */ + _ODBC_LogODBCError( (HSTMT)0 ); + } + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, + "ODBC Driver: Initialised: (User=%s, Pwd=, Srv=%s, DB=%s)", + ODBC.szUserName, ODBC.szServer, ODBC.szDatabase); + + /* Return result code to caller. + */ + return(SDD_OK); +} + +/****************************************************************************** + * Function: odbc_CloseService + * Description: Entry point which performs a drive closedown. The closedown + * procedure ensure that the driver returns to a virgin state + * (ie.like at power up) so that InitService can be called again. + * + * Returns: SDD_FAIL- An error occurred in closing the driver and an + * error message is stored in szErrMsg. + * SDD_OK - Driver successfully closed. + ******************************************************************************/ +int odbc_CloseService( UCHAR *szErrMsg ) /* O: Error message if failed */ +{ + /* Local variables. + */ + RETCODE nResult; + UCHAR *szFunc = "odbc_CloseService"; + + /* Drop connection to data source. + */ + nResult=SQLDisconnect(ODBC.hDbc); + if( nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + sprintf(szErrMsg, "%s: Couldnt drop connection to data source", + SDD_EMSG_BADEXIT); + return(SDD_FAIL); + } + + /* Free up connection memory. + */ + nResult=SQLFreeConnect(ODBC.hDbc); + if( nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + sprintf(szErrMsg, "%s: Couldnt free connection memory", + SDD_EMSG_BADFREE); + return(SDD_FAIL); + } + + /* Free up environment memory. + */ + nResult=SQLFreeEnv(ODBC.hEnv); + if( nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO ) + { + sprintf(szErrMsg, "%s: Couldnt free environment memory", + SDD_EMSG_BADFREE); + return(SDD_FAIL); + } + + /* Tidy up variables. + */ + ODBC.nAbortPending = FALSE; + strcpy(ODBC.szUserName, ""); + strcpy(ODBC.szPassword, ""); + strcpy(ODBC.szServer, ""); + strcpy(ODBC.szDatabase, ""); + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "ODBC Driver: Closed."); + + /* Return result code to caller. + */ + return(SDD_OK); +} + +/****************************************************************************** + * Function: odbc_ProcessRequest + * Description: Entry point into driver to initiate the driver into + * processing a request. A data block is passed as a parameter + * to the driver which represents a request with relevant + * parameters. The data within the structure is only relevant + * to the original client and this driver code. + * + * Returns: SDD_FAIL- An error occurred within the driver whilst trying to + * process the request, see error text. + * SDD_OK - Request processed successfully. + ******************************************************************************/ +int odbc_ProcessRequest( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply*/ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "odbc_ProcessRequest"; + + /* If the request block doesnt contain any data, something went wrong + * somewhere?? + */ + if(nDataLen < 1) + { + sprintf(szErrMsg, + "%s: Illegal request, has size of %d Bytes", + SDD_EMSG_BADREQ, nDataLen); + return(SDD_FAIL); + } + + /* First byte of the request data block indicates actions required, so + * decipher it. + */ + switch(snzDataBuf[0]) + { + /* Request to execute a block of SQL code. SQL immediately follows the + * command byte in the input buffer. + */ + case SDD_RUNSQL: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, "ODBC Driver: Requested to execute SQL"); + + /* Execute the SQL. + */ + nReturn=_ODBC_RunSql(&snzDataBuf[1],nDataLen,fSendDataCB,szErrMsg); + break; + + case SDD_LIST_DB: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "ODBC Driver: Requested to list all Databases in data source"); + + /* Call function to extract names of all databases in data source. + */ + nReturn=_ODBC_ListDB(fSendDataCB, szErrMsg); + break; + + case SDD_LIST_TABLES: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "ODBC Driver: Requested to list all tables for a database"); + + /* Call function to extract names of all tables in a database. + */ + nReturn=_ODBC_ListTables(&snzDataBuf[1], nDataLen, fSendDataCB, + szErrMsg); + break; + + case SDD_LIST_COLS: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "ODBC Driver: Requested to list all columns for a table"); + + /* Call function to extract details of all columns in a table. + */ + nReturn=_ODBC_ListCols(&snzDataBuf[1], nDataLen, fSendDataCB, + szErrMsg); + break; + + default: + sprintf(szErrMsg, + "%s: Illegal command in request buffer (%x)", + SDD_EMSG_BADCMD, snzDataBuf[0]); + return(SDD_FAIL); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: odbc_ProcessOOB + * Description: Entry point into driver to process an out of band command + * that may or may not be relevant to current state of + * operation. The task of this function is to decipher the + * command and act on it immediately, ie. a cancel command + * would abort any ProcessRequest that is in process and + * clean up. + * + * Returns: No returns. + ******************************************************************************/ +void odbc_ProcessOOB( UCHAR nCommand ) /* I: OOB Command */ +{ + /* Local variables. + */ + + /* Decipher command and perform actions accordingly. + */ + switch(nCommand) + { + /* Request to abort current ProcessRequest command and return daemon + * to a waiting-for-request state. + */ + case SDD_ABORT: + break; + + default: + break; + } + + /* Return to caller. + */ + return; +} diff --git a/SDD/sdd_odbc.h b/SDD/sdd_odbc.h new file mode 100755 index 0000000..684dd65 --- /dev/null +++ b/SDD/sdd_odbc.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_odbc.h + * Description: Server Data-source Driver library driver header file for + * odbc driver. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef SDD_ODBC_H +#define SDD_ODBC_H + +/* Definitions for maxims etc. +*/ +#define MAX_DSN_DESCR_LEN 255 +#define MAX_TABLE_DESCR_LEN 255 + +/* Definitions for constants, configurable options etc. +*/ +#define ODBC_COLFILTER "COLUMN=" +#define ODBC_DBNAME "DBNAME=" +#define ODBC_TABLENAME "TABLENAME=" + +/* Structure for variables internal to this driver module. +*/ +typedef struct { + UINT nAbortPending; /* An abort is pending */ + UCHAR szUserName[MAX_USERNAMELEN]; /* Name of user that this */ + /* module will use when */ + /* connecting to data source. */ + UCHAR szPassword[MAX_PASSWORDLEN]; /* Password that this module */ + /* will use when connecting */ + /* to data source. */ + UCHAR szServer[MAX_SERVERNAMELEN]; /* Name of Data Source server */ + UCHAR szDatabase[MAX_DBNAMELEN]; /* Name of Data Source data */ + /* base. */ + HENV hEnv; /* Environment buffer for ODBC*/ + HDBC hDbc; /* ODBC connection handle */ +} ODBC_DRIVER; + +/* Allocate any global variables needed within this module. +*/ +static ODBC_DRIVER ODBC; + +/* Prototypes of internal functions, not seen by any outside module. +*/ +void _ODBC_LogODBCError( HSTMT ); +int _ODBC_GetArg( UCHAR *, UCHAR *, int, UCHAR ** ); +int _ODBC_RunSql( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +int _ODBC_ListDB( int (*)(UCHAR *, UINT), UCHAR * ); +int _ODBC_ListTables( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +int _ODBC_ListCols( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); + +#endif /* SDD_ODBC_H */ diff --git a/SDD/sdd_scmd.c b/SDD/sdd_scmd.c new file mode 100755 index 0000000..c4dc5cc --- /dev/null +++ b/SDD/sdd_scmd.c @@ -0,0 +1,1282 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_scmd.c + * Description: Server Data-source Driver library driver to handle system + * command execution on the local host. + * + * 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 + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) +#include +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define SDD_SCMD_C + +/* Bring in local specific header files. +*/ +#include "sdd.h" +#include "sdd_scmd.h" + +/****************************************************************************** + * Function: _SCMD_GetStrArg + * Description: Function to scan an input buffer and extract a string based + * argument. + * + * Returns: SDD_FAIL- Couldnt obtain argument. + * SDD_OK - Argument obtained. + ******************************************************************************/ +int _SCMD_GetStrArg( UCHAR *snzDataBuf, /* I: Input buffer */ + int nDataLen, /* I: Len of data */ + UCHAR *szArg, /* I: Arg to look for */ + UCHAR **pszPath ) /* O: Pointer to argument */ +{ + /* Local variables. + */ + int nCmpLen; + int nPos; + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_SCMD_GetStrArg"; + + /* Go through the input buffer looking for 'szArg' within the + * input buffer. If it exists, then note the position just after it + * as this contains the string argument. + */ + for(nPos=0, nCmpLen=strlen(szArg); nPos < nDataLen; nPos++) + { + /* If a match occurs, then setup the pointer to correct location. + */ + if(strncmp(&snzDataBuf[nPos], szArg, nCmpLen) == 0) + { + /* Make sure that the match is not a sub-match as some of the + * variables we are scanning for are similar. + */ + if( (nPos == 0) || + (nPos > 0 && snzDataBuf[nPos-1] == '\0') || + (nPos > 0 && isspace(snzDataBuf[nPos-1])) ) + { + nPos += nCmpLen; + break; + } + } + } + + /* If the pointer did not reach the end of the buffer then we have + * located a valid name. + */ + if(nPos < nDataLen) + { + /* Setup the callers pointer to point to the correct location + * in the buffer. + */ + *pszPath = &snzDataBuf[nPos]; + nReturn=SDD_OK; + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SCMD_ValidatePath + * Description: Function to validate the existence of a path. + * + * Returns: SDD_FAIL- Couldnt validate PATH. + * SDD_OK - PATH validated. + ******************************************************************************/ +int _SCMD_ValidatePath( UCHAR *pszPath ) /* I: Path to validate */ +{ + /* Local variables. + */ + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_SCMD_ValidatePath"; + struct stat sStat; + + /* Test the path to ensure its valid. + */ + if( (stat(pszPath, &sStat) == -1) || + ((sStat.st_mode & S_IFDIR) == 0) ) + { + nReturn=SDD_FAIL; + } else + { + nReturn=SDD_OK; + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SCMD_ValidateFile + * Description: Function to validate the existence of a file or to validate + * that a file can be created. + * + * Returns: SDD_FAIL- Couldnt obtain Filename or validate it. + * SDD_OK - Filename obtained and validated. + ******************************************************************************/ +int _SCMD_ValidateFile( UCHAR *pszPath, /* I: Path to file */ + UCHAR *pszFile, /* I: File to validate */ + UINT nWriteFlag ) /* I: Read = 0, Write = 1 */ +{ + /* Local variables. + */ + int nReturn = SDD_FAIL; + UCHAR szTmpBuf[MAX_TMPBUFLEN]; + UCHAR *szFunc = "_SCMD_ValidateFile"; + struct stat sStat; + FILE *fpTest; + + /* Concatenate the file and path together, ready to perform a test. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + sprintf(szTmpBuf, "%s//%s", pszPath, pszFile); +#endif +#if defined(_WIN32) + sprintf(szTmpBuf, "%s%c%s", pszPath, 0x5c, pszFile); +#endif + + /* Is the file to be created? + */ + if(nWriteFlag == TRUE) + { + /* Test the file by trying to create it, see if the underlying OS + * can perform this operation. + */ + if((fpTest=fopen(szTmpBuf, "w")) != NULL) + { + /* Close and remove the file we created, not needed yet. + */ + fclose(fpTest); + unlink(szTmpBuf); + nReturn = SDD_OK; + } else + { + nReturn = SDD_FAIL; + } + } else + { + /* Test the file to ensure that it exists and is readable. + */ + if( (stat(szTmpBuf, &sStat) == -1) || + ((sStat.st_mode & S_IFREG) == 0) ) + { + nReturn=SDD_FAIL; + } else + { + nReturn=SDD_OK; + } + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SCMD_ValidateTime + * Description: Function to validate a time value given as an ascii string. + * + * Returns: SDD_FAIL- Couldnt obtain a TIME or validate it. + * SDD_OK - TIME obtained and validated. + ******************************************************************************/ +int _SCMD_ValidateTime( UCHAR *pszTime, /* I: Time to verify */ + ULNG *lTime ) /* O: Time in seconds */ +{ + /* Local variables. + */ + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_SCMD_ValidateTime"; + + /* Convert the time value into a long value which represents the + * number of seconds since the beginning of the machine date start + * point (ie. 0:00 Jan 1970 for PCDOS). + */ + nReturn=SDD_OK; + + /* Return result code to caller. + */ + return(SDD_OK); +} + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) +/****************************************************************************** + * Function: _SCMD_Exec + * Description: Function to execute a given command via a fork and exec, + * attaching the parent to the childs I/O so that any data + * output by the child can be captured by the parent and fed + * back to the caller. + * + * Returns: SDD_FAIL- Command failed during execution. + * SDD_OK - Command executed successfully. + ******************************************************************************/ +int _SCMD_Exec( int nTimedExec, /* I: Is this a timed exec (T/F)? */ + UCHAR *pszPath, /* I: Path to command */ + UCHAR *pszCmd, /* I: Command name */ + UCHAR *pszArgs, /* I: Arguments to command */ + ULNG lTimeToExec, /* I: Time to execution */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: Func for returning data */ + UCHAR *szErrMsg ) /* O: Error message generated */ +{ + /* Local variables. + */ + int nChar; + int nEnd = FALSE; + int nNdx; + int nResult; + int nRetBufSize; + int nReturn = SDD_OK; + UCHAR *pszTmpBuf; + UCHAR *pszRetBuf; + UCHAR *szFunc = "_SCMD_Exec"; + FILE *fpStdIn; + + /* Work out return buffer memory based on mode and return buffer size. + */ + if(SCMD.nRetMode == SCMD_BINMODE) + { + nRetBufSize = SCMD.nRetBufSize+1; + } else + { + nRetBufSize = MAX_RETURN_BUF_SIZE+1; + } + + /* Allocate memory for return buffer. + */ + if((pszRetBuf=(UCHAR *)malloc(nRetBufSize)) == NULL) + { + return(SDD_FAIL); + } + + /* Is this a timed execution? + */ + if(nTimedExec == TRUE) + { + sleep(1); + } + + /* Concatenate the file, command and arguments to build up a string + * for the system command to use. + */ + nNdx=strlen(pszPath)+strlen(pszCmd)+strlen(pszArgs)+10; + if((pszTmpBuf=(UCHAR *)malloc(nNdx)) == NULL) + { + return(SDD_FAIL); + } + sprintf(pszTmpBuf, "%s//%s %s", pszPath, pszCmd, pszArgs); + + /* Execute the program as a child with its stdout connected to a read + * stream such that we can capture its output. + */ + if((fpStdIn=popen(pszTmpBuf, "r")) != NULL) + { + while(nEnd == FALSE) + { + /* Initialise loop variables. + */ + nNdx=1; + + /* Loop until our return buffer becomes full or we run out of data + * to read. + */ + while(nNdx < nRetBufSize && (nChar=fgetc(fpStdIn)) != EOF) + { + pszRetBuf[nNdx] = (UCHAR)nChar; + nNdx++; + + /* If we are in ascii mode and we detect an end of line then + * exit from the loop. + */ + if(SCMD.nRetMode == SCMD_ASCIIMODE && pszRetBuf[nNdx-1] == '\n') + break; + } + + /* Has the child completed, i.e. no more data? + */ + if(nChar == EOF) + { + /* Set flag to indicate that we have reached end of input. + */ + nEnd = TRUE; + } + + /* If data exists in the buffer for xmit, then transmit it. + */ + if(nNdx > 1) + { + /* Set the first location in the return buffer to indicate + * that this is just one of many data blocks in consecutive + * order. + */ + pszRetBuf[0] = SDD_DATABLOCK; + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(pszRetBuf, nNdx) == SDD_FAIL) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + nReturn = SDD_FAIL; + nEnd = TRUE; + } + } + } + + /* Close the process and get its exit code. + */ + nResult=pclose(fpStdIn); + + /* If we completed without error, then build a final packet + * to indicate the exit code of the process. + */ + if(nEnd == TRUE && nReturn != SDD_FAIL) + { + /* Create the packet for transmit based on the end of data + * indicator and an ascii representation of the exit code. + */ + pszRetBuf[0] = SDD_ENDBLOCK; + sprintf(&pszRetBuf[1], "%d", nResult); + + /* Send the packet and see what happens. + */ + if(fSendDataCB(pszRetBuf, (strlen(&pszRetBuf[1])+1)) == SDD_FAIL) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + nReturn = SDD_FAIL; + } + } + } else + { + /* Build up an error message to indicate that the command was + * invalid. + */ + sprintf(szErrMsg, "%s: Command execution failed", SDD_EMSG_BADCMD); + + /* Set exit code to indicate failure. + */ + nReturn = SDD_FAIL; + } + + /* Free up allocated memory block. + */ + free(pszTmpBuf); + + /* Return result code to caller. + */ + return(nReturn); +} +#endif + +/****************************************************************************** + * Function: _SCMD_GetWriteData + * Description: Function to scan an input buffer, verify that it has data in it, + * extract the data and store in the opened file stream and + * set the start flag if the block is the final block. + * + * Returns: SDD_FAIL- Bad block of data or error writing to file. + * SDD_OK - Block obtained and stored. + ******************************************************************************/ +int _SCMD_GetWriteData( UCHAR *snzDataBuf, /* I: Input buffer */ + int nDataLen, /* I: Len of data */ + FILE *fpFile, /* IO: Opened file stream */ + int *nLast, /* O: Last block flag */ + UCHAR *szErrMsg ) /* O: Any resultant error msg */ +{ + /* Local variables. + */ + int nBlockSize; + int nCmpDataLen; + int nCmpEndLen; + int nPos; + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_SCMD_GetWriteData"; + + /* Initialise any required variables. + */ + *nLast = FALSE; + nCmpDataLen=strlen(SCMD_DATA); + nCmpEndLen=strlen(SCMD_END); + + /* Go through the input buffer looking for SCMD_DATA or SCMD_END within the + * input buffer. If either exist then note position just after it + * as this contains the size of the data block as an ascii number. If + * SCMD_END is detected then set the nLast flag to indicate last block. + */ + for(nPos=0; nPos < nDataLen; nPos++) + { + /* If a match occurs, then setup the pointer to correct location. + */ + if(strncmp(&snzDataBuf[nPos], SCMD_DATA, nCmpDataLen) == 0 || + strncmp(&snzDataBuf[nPos], SCMD_END, nCmpEndLen) == 0) + { + /* Make sure that the match is not a sub-match as some of the + * variables we are scanning for are similar. + */ + if( (nPos == 0) || + (nPos > 0 && snzDataBuf[nPos-1] == '\0') || + (nPos > 0 && isspace(snzDataBuf[nPos-1])) ) + { + if(strncmp(&snzDataBuf[nPos], SCMD_DATA, nCmpDataLen) == 0) + { + nPos += nCmpDataLen; + } else + { + /* Setup the nLast flag as this is the last data block. + */ + *nLast = TRUE; + nPos += nCmpEndLen; + } + break; + } + } + } + + /* If the pointer did not reach the end of the buffer then we have + * located a valid data block. + */ + if(nPos < nDataLen) + { + /* The following location upto a NULL byte should contain the + * size of the data block in ASCII representation so use scanf + * to extract the data accordingly. + */ + if(sscanf(&snzDataBuf[nPos], "%d", &nBlockSize) != 1) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, "%s: Invalid Block Size provided", + SDD_EMSG_BADBLOCKSZ); + nReturn = SDD_FAIL; + goto _SCMD_GetWriteData_EXIT; + } + + /* Update our index variable. + */ + nPos += (strlen(&snzDataBuf[nPos]) + 1); + + /* Quick test before we go any further, ensure that the data + * provided is sufficient. + */ + if((nPos+nBlockSize) != nDataLen) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, + "%s: Data block size does not match data provided", + SDD_EMSG_BADBLOCKSZ); + nReturn = SDD_FAIL; + goto _SCMD_GetWriteData_EXIT; + } + + /* Read data byte at a time and write to the opened file stream. + */ + while(nPos < nDataLen) + { + if(fputc(snzDataBuf[nPos], fpFile) == EOF) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, + "%s: Couldnt write to temporary file to store data", + SDD_EMSG_FILEERROR); + nReturn = SDD_FAIL; + break; + } + + /* Time for pointer update... Could write the same data forever + * I suppose.... + */ + nPos += 1; + } + } + + /* I suppose we are successful.... this time! + */ + nReturn=SDD_OK; + +_SCMD_GetWriteData_EXIT: + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SCMD_PutReadData + * Description: Function to read an open stream and transmit the data + * contents to the caller via the callback mechanism. + * + * Returns: SDD_FAIL- Couldnt obtain PATH. + * SDD_OK - PATH obtained. + ******************************************************************************/ +int _SCMD_PutReadData( FILE *fpFile, /* I: Stream to read from */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send data to */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nChar; + int nEnd = FALSE; + int nNdx; + int nReturn; + UCHAR szRetBuf[SCMD.nRetBufSize+1]; + UCHAR *szFunc = "_SCMD_PutReadData"; + + /* Loop, extracting data from the input stream and sending it to the + * users callback in packets of size indicated by SCMD.nRetBufSize. + */ + while(nEnd == FALSE) + { + /* Initialise any loop variables. + */ + nNdx=1; + + /* Loop until the return buffer becomes full or we run out of data. + */ + while(nNdx < SCMD.nRetBufSize && (nChar=fgetc(fpFile)) != EOF) + { + szRetBuf[nNdx] = (UCHAR)nChar; + nNdx++; + } + + /* Is this the end of the input stream? + */ + if(nChar == EOF) + { + /* Set flag as we've got to end of the file, time to get out. + */ + nEnd = TRUE; + + /* Set the first location in the input buffer to indicate last + * block. + */ + szRetBuf[0] = SDD_ENDBLOCK; + } else + { + /* Set the first location in the input buffer to indicate that + * this is one of many data blocks. + */ + szRetBuf[0] = SDD_DATABLOCK; + } + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(szRetBuf, nNdx) == SDD_FAIL) + { + /* Create an error message to indicate the problem. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", SDD_EMSG_SENDROW); + nReturn = SDD_FAIL; + nEnd = TRUE; + } + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SCMD_MoveFile + * Description: Function to move a file from one location/name to another. + * This is performed as a copy and unlink operation because the + * underlying may not support moves across file systems. + * + * Returns: SDD_FAIL- An error whilst moving file, see szErrMsg. + * SDD_OK - File moved successfully. + ******************************************************************************/ +int _SCMD_MoveFile( UCHAR *pszSrcPath, /* I: Path to source file */ + UCHAR *pszSrcFile, /* I: Source File */ + UCHAR *pszDstPath, /* I: Path to dest file */ + UCHAR *pszDstFile, /* I: Dest File */ + UCHAR *szErrMsg ) /* O: Error message */ +{ + /* Local variables. + */ + int nChar; + int nReturn = SDD_OK; + UCHAR szSourceFile[MAX_FILENAME_LEN+1]; + UCHAR szDestFile[MAX_FILENAME_LEN+1]; + UCHAR *szFunc = "_SCMD_MoveFile"; + FILE *fpDestFile; + FILE *fpSourceFile; + + /* Combine path and filename to create an absolute source filename. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + sprintf(szSourceFile, "%s//%s", pszSrcPath, pszSrcFile); +#endif +#if defined(_WIN32) + sprintf(szSourceFile, "%s%c%s", pszSrcPath, 0x5c, pszSrcFile); +#endif + + /* Combine path and filename to create an absolute destination filename. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + sprintf(szDestFile, "%s//%s", pszDstPath, pszDstFile); +#endif +#if defined(_WIN32) + sprintf(szDestFile, "%s%c%s", pszDstPath, 0x5c, pszDstFile); +#endif + + /* Open source file ready for copy operation. + */ + if((fpSourceFile=fopen(szSourceFile, "r")) == NULL) + { + sprintf(szErrMsg, "%s: Couldnt open source file (%s)", szSourceFile); + return(SDD_FAIL); + } + + /* Open destination file ready to accept copied data. + */ + if((fpDestFile=fopen(szDestFile, "w")) == NULL) + { + fclose(fpSourceFile); + sprintf(szErrMsg, "%s: Couldnt create destination file (%s)", + szDestFile); + return(SDD_FAIL); + } + + /* Copy data byte by byte, checking for accuracy. + */ + while((nChar=fgetc(fpSourceFile)) != EOF) + { + /* Put the newly read byte into the destination file and check return + * status. If the put fails, exit with error. + */ + if(fputc((UCHAR)nChar, fpDestFile) == EOF) + { + /* Close files, and delete destination. + */ + fclose(fpSourceFile); + fclose(fpDestFile); + unlink(szDestFile); + + /* Build up error message and exit. + */ + sprintf(szErrMsg, "%s: Error writing to destination file (%s)", + szDestFile); + return(SDD_FAIL); + } + } + + /* All done, first close and confirm destination file, then close and + * delete source file. + */ + fclose(fpSourceFile); + if(fclose(fpDestFile) == EOF) + { + sprintf(szErrMsg, "%s: Error writing to destination file (%s)", + szDestFile); + return(SDD_FAIL); + } + unlink(szSourceFile); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SCMD_DeleteFile + * Description: Function to delete a file from the given path. + * + * Returns: SDD_FAIL- An error whilst moving file, see szErrMsg. + * SDD_OK - File moved successfully. + ******************************************************************************/ +int _SCMD_DeleteFile( UCHAR *pszDelPath, /* I: Path to file */ + UCHAR *pszDelFile, /* I: File to delete */ + UCHAR *szErrMsg ) /* O: Error message */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR szDeleteFile[MAX_FILENAME_LEN+1]; + UCHAR *szFunc = "_SCMD_DeleteFile"; + + /* Combine path and filename to create an absolute filename for deletion. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + sprintf(szDeleteFile, "%s//%s", pszDelPath, pszDelFile); +#endif +#if defined(_WIN32) + sprintf(szDeleteFile, "%s%c%s", pszDelPath, 0x5c, pszDelFile); +#endif + + /* Call the operating system to perform the actual operation. + */ + unlink(szDeleteFile); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: scmd_InitService + * Description: Entry point which initialises the driver into a defined state. + * It is mandatory that this function is called before any other + * in order for the driver to function correctly. The caller + * provides it with two types of data, 1) A structure containing + * data for it to use in initialising itself, 2) a pointer to a + * buffer which the driver uses to place an error message should + * it not be able to complete initialisation. + * + * Returns: SDD_FAIL- An error occurred in initialising the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver initialised successfully. + ******************************************************************************/ +int scmd_InitService( SERVICEDETAILS *sServiceDet, /* I: Init data */ + UCHAR *szErrStr ) /* O: Error message */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "scmd_InitService"; + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "SCMD Driver: Initialised:"); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: scmd_CloseService + * Description: Entry point which performs a drive closedown. The closedown + * procedure ensure that the driver returns to a virgin state + * (ie.like at power up) so that InitService can be called again. + * + * Returns: SDD_FAIL- An error occurred in closing the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver successfully closed. + ******************************************************************************/ +int scmd_CloseService( UCHAR *szErrMsg ) /* O: Error message if failed */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "scmd_CloseService"; + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "SCMD Driver: Closed."); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: scmd_ProcessRequest + * Description: Entry point into driver to initiate the driver into + * processing a request. A data block is passed as a parameter + * to the driver which represents a request with relevant + * parameters. The data within the structure is only relevant + * to the original client and this driver code. + * + * Returns: SDD_FAIL- An error occurred within the driver whilst trying to + * process the request, see error text. + * SDD_OK - Request processed successfully. + ******************************************************************************/ +int scmd_ProcessRequest( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply*/ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Static variables. + */ + static UCHAR szWriteFile[MAX_FILENAME_LEN+1]; + static FILE *fpWriteFile = NULL; + + /* Local variables. + */ + int nLastBlock; + int nReturn = SDD_OK; + ULNG lTime = 0; + UCHAR *pszDstFile; + UCHAR *pszSrcFile; + UCHAR *pszDstPath; + UCHAR *pszSrcPath; + UCHAR *pszArgs; + UCHAR *pszBufSize; + UCHAR *pszCmd; + UCHAR *pszMode; + UCHAR *pszTime; + UCHAR szReadFile[MAX_FILENAME_LEN+1]; + UCHAR *szFunc = "scmd_ProcessRequest"; + FILE *fpReadFile; + + /* If the request block doesnt contain any data, something went wrong + * somewhere? + */ + if(nDataLen <= 1) + { + sprintf(szErrMsg, + "%s: Illegal request, has size of %dBytes", + SDD_EMSG_BADREQ, nDataLen); + return(SDD_FAIL); + } + + /* If we have an EXECUTE command, then extract relevant information from + * buffer. + */ + if(snzDataBuf[0] == SDD_CMD_EXEC || snzDataBuf[0] == SDD_CMD_EXEC_TIMED) + { + /* Locate the path argument and validate it. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_SRCPATH, + &pszSrcPath) == SDD_FAIL|| + _SCMD_ValidatePath(pszSrcPath) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid PATH to command provided", + SDD_EMSG_BADPATH); + return(SDD_FAIL); + } + + /* Locate the command argument and validate it. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_CMD, &pszCmd) + == SDD_FAIL || + _SCMD_ValidateFile(pszSrcPath, pszCmd, FALSE) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid COMMAND provided", + SDD_EMSG_BADCMD); + return(SDD_FAIL); + } + + /* Locate the mode flag (ASCII or BINARY) and if present ensure + * it is valid. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_MODE, &pszMode) + == SDD_OK) + { + /* Is the parameter valid? + */ + if( strcmp(pszMode, SCMD_BINARY) != 0 && + strcmp(pszMode, SCMD_ASCII) !=0 ) + { + sprintf(szErrMsg, "%s: Invalid MODE provided", SDD_EMSG_BADCMD); + return(SDD_FAIL); + } + + /* Setup parameter accordingly. + */ + if( strcmp(pszMode, SCMD_BINARY) == 0 ) + { + SCMD.nRetMode = SCMD_BINMODE; + } else + SCMD.nRetMode = SCMD_ASCIIMODE; + } else + { + /* Setup the default return buffer mode. + */ + SCMD.nRetMode = DEF_RETURN_MODE; + } + + /* Locate the buffer size flag and if present, ensure its within range. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1,SCMD_BUFSIZE,&pszBufSize) + == SDD_OK ) + { + /* Get the value passed to verify and store. + */ + sscanf(pszBufSize, "%d", &SCMD.nRetBufSize); + if( SCMD.nRetBufSize < MIN_RETURN_BUF_SIZE || + SCMD.nRetBufSize > MAX_RETURN_BUF_SIZE ) + { + sprintf(szErrMsg, "%s: Invalid MODE provided", SDD_EMSG_BADCMD); + return(SDD_FAIL); + } + } else + { + /* Setup the default return buffer size. + */ + SCMD.nRetBufSize = DEF_RETURN_BUF_SIZE; + } + + /* Locate the Args argument. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_ARGS, &pszArgs) + == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid command ARGS provided", + SDD_EMSG_BADARGS); + return(SDD_FAIL); + } + + /* For timed EXECUTION commands, get the time value. + */ + if(snzDataBuf[0] == SDD_CMD_EXEC_TIMED) + { + /* Locate the Time argument in the input buffer, validate that it + * is for a time in the future and setup our pointer into it. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_TIME, &pszTime) + == SDD_FAIL || + _SCMD_ValidateTime(pszTime, &lTime) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid TIME value provided", + SDD_EMSG_BADARGS); + return(SDD_FAIL); + } + } + } + + /* If we have a READ/MOVE command, then extract relevant information from + * buffer. + */ + if(snzDataBuf[0] == SDD_CMD_READ || snzDataBuf[0] == SDD_CMD_MOVE) + { + /* Locate the path argument and validate it. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_SRCPATH, + &pszSrcPath) == SDD_FAIL || + _SCMD_ValidatePath(pszSrcPath) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid Source PATH provided", + SDD_EMSG_BADPATH); + return(SDD_FAIL); + } + + /* Locate the file argument and validate it. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_SRCFILE, + &pszSrcFile) == SDD_FAIL || + _SCMD_ValidateFile(pszSrcPath, pszSrcFile, FALSE) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid Source FILE provided", + SDD_EMSG_BADPATH); + return(SDD_FAIL); + } + } + + /* If we have a DELETE/MOVE command, then extract relevant information + * from buffer. + */ + if(snzDataBuf[0] == SDD_CMD_DEL || snzDataBuf[0] == SDD_CMD_MOVE) + { + /* Locate the path argument and validate it. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_DSTPATH, + &pszDstPath) == SDD_FAIL || + _SCMD_ValidatePath(pszDstPath) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid Destination PATH provided", + SDD_EMSG_BADPATH); + return(SDD_FAIL); + } + + /* Locate the file argument and validate it. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_DSTFILE, + &pszDstFile) == SDD_FAIL || + _SCMD_ValidateFile(pszDstPath, pszDstFile, + (snzDataBuf[0] == SDD_CMD_DEL ? FALSE : TRUE)) + == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid Destination FILE provided", + SDD_EMSG_BADPATH); + return(SDD_FAIL); + } + } + + /* First byte of the request data block indicates actions required, so + * decipher it. + */ + switch(snzDataBuf[0]) + { + case SDD_CMD_EXEC: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "SCMD Driver: Req to execute COMMAND"); + Lgr(LOG_DEBUG, szFunc, + "Command: Path=%s, Cmd=%s, Args=%s", + pszSrcPath, pszCmd, pszArgs); + + /* Execute the required command. Any output generated by the + * command is fed back to the caller row-at-a-time. The final + * exit status dictates wether the command was a success or + * failure. + */ + if(_SCMD_Exec(FALSE,pszSrcPath,pszCmd,pszArgs,0,fSendDataCB, + szErrMsg) == SDD_FAIL) + { + return(SDD_FAIL); + } + break; + + case SDD_CMD_EXEC_TIMED: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "SCMD Driver: Req to execute timed COMMAND"); + Lgr(LOG_DEBUG, szFunc, + "Command: Path=%s, Cmd=%s, Args=%s, Time=%ld", + pszSrcPath, pszCmd, pszArgs, lTime); + + /* Execute the required command at the given time, and upon + * command completion return the status to the caller via a return + * code and text message. + */ + if(_SCMD_Exec(TRUE,pszSrcPath,pszCmd,pszArgs,lTime,fSendDataCB, + szErrMsg) == SDD_FAIL) + { + return(SDD_FAIL); + } + break; + + case SDD_CMD_READ: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "SCMD Driver: Requested to perform a READ operation"); + + /* Open the file as indicated by the command parameters. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + sprintf(szReadFile, "%s//%s", pszSrcPath, pszSrcFile); +#endif +#if defined(_WIN32) + sprintf(szReadFile, "%s%c%s", pszSrcPath, 0x5c, pszSrcFile); +#endif + if((fpReadFile=fopen(szReadFile, "r")) == NULL) + { + /* Create a message to indicate failure. + */ + sprintf(szErrMsg, + "%s: Illegal Filename given in command", + SDD_EMSG_BADFILE); + + /* Exit directly as we have nothing open to tidy up. + */ + return(SDD_FAIL); + } + + /* Copy the requested file from opened file stream to the remote + * via a return byte stream. + */ + nReturn=_SCMD_PutReadData(fpReadFile, fSendDataCB, szErrMsg); + + /* Close the file as were all done, then exit. + */ + fclose(fpReadFile); + break; + + case SDD_CMD_WRITE: + /* If this is the first time we've been called then process + * options and open the indicated file for writing. + */ + if(fpWriteFile == NULL) + { + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "SCMD Driver: Requested to perform a WRITE operation"); + + /* Locate the path argument and validate it. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_DSTPATH, + &pszDstPath) == SDD_FAIL || + _SCMD_ValidatePath(pszDstPath) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid Destination PATH provided", + SDD_EMSG_BADPATH); + return(SDD_FAIL); + } + + /* Locate the file argument and validate it. + */ + if(_SCMD_GetStrArg(&snzDataBuf[1], nDataLen-1, SCMD_DSTFILE, + &pszDstFile) == SDD_FAIL || + _SCMD_ValidateFile(pszDstPath, pszDstFile, TRUE) == SDD_FAIL) + { + sprintf(szErrMsg, "%s: Invalid Destination FILE provided", + SDD_EMSG_BADPATH); + return(SDD_FAIL); + } + + /* Create new file for writing. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + sprintf(szWriteFile, "%s//%s", pszDstPath, pszDstFile); +#endif +#if defined(_WIN32) + sprintf(szWriteFile, "%s%c%s", pszDstPath, 0x5c, pszDstFile); +#endif + if((fpWriteFile=fopen(szWriteFile, "w")) == NULL) + { + /* Create message to indicate failure. + */ + sprintf(szErrMsg, "%s: Couldnt create file", + SDD_EMSG_BADPATH); + return(SDD_FAIL); + } + } else + { + /* Extract the data from the passed buffer and store in the + * opened file. Make a note as to wether this is the last + * block or not. + */ + if(_SCMD_GetWriteData(&snzDataBuf[1], (nDataLen-1), fpWriteFile, + &nLastBlock, szErrMsg) == SDD_FAIL) + { + /* Tidy up as we are not coming back!!!! + */ + fclose(fpWriteFile); + fpWriteFile = NULL; + + /* Delete the file as theres no point in keeping half a + * file. + */ + unlink(szWriteFile); + + /* Exit with the bad tidings in szErrMsg. + */ + return(SDD_FAIL); + } + + /* If we wrote the last block successfully then alles klare, + * wunderbar!. + */ + if(nLastBlock == TRUE) + { + /* Firstly, close the opened file and reset the pointer for + * next time. + */ + fclose(fpWriteFile); + fpWriteFile = NULL; + } + } + break; + + case SDD_CMD_MOVE: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "SCMD Driver: Requested to perform a MOVE operation"); + + /* Call function to perform the move and use its return code + * as our return code. + */ + nReturn=_SCMD_MoveFile(pszSrcPath, pszSrcFile, pszDstPath, + pszDstFile, szErrMsg); + break; + + case SDD_CMD_DEL: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "SCMD Driver: Requested to perform a DELETE operation"); + + /* Call function to perform the move and use its return code + * as our return code. + */ + nReturn=_SCMD_DeleteFile(pszDstPath, pszDstFile, szErrMsg); + break; + + default: + sprintf(szErrMsg, + "%s: Illegal command in request buffer (%x)", + SDD_EMSG_BADREQ, snzDataBuf[0]); + return(SDD_FAIL); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: scmd_ProcessOOB + * Description: Entry point into driver to process an out of band command + * that may or may not be relevant to current state of + * operation. The task of this function is to decipher the + * command and act on it immediately, ie. a cancel command + * would abort any ProcessRequest that is in process and + * clean up. + * + * Returns: No returns. + ******************************************************************************/ +void scmd_ProcessOOB( UCHAR nCommand ) /* I: OOB Command */ +{ + /* Local variables. + */ + + /* Decipher command and perform actions accordingly. + */ + switch(nCommand) + { + /* Request to abort current ProcessRequest command and return daemon + * to a waiting-for-request state. + */ + case SDD_ABORT: + break; + + /* Request to close down and exit. + */ + case SDD_EXIT: + break; + + default: + break; + } + + /* Return to caller. + */ + return; +} diff --git a/SDD/sdd_scmd.h b/SDD/sdd_scmd.h new file mode 100755 index 0000000..aa82975 --- /dev/null +++ b/SDD/sdd_scmd.h @@ -0,0 +1,87 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_scmd.h + * Description: Server Data-source Driver library driver header file for + * the system command execution driver. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef SDD_SCMD_H +#define SDD_SCMD_H + +/* Definitions for constants, configurable options etc. +*/ +#define SCMD_ASCII "ASCII" +#define SCMD_ARGS "ARGS=" +#define SCMD_BINARY "BINARY" +#define SCMD_CMD "COMMAND=" +#define SCMD_DATA "DATA=" +#define SCMD_END "END=" +#define SCMD_MODE "MODE=" +#define SCMD_BUFSIZE "BUFSIZE=" +#define SCMD_TIME "TIME=" +#define SCMD_SRCPATH "SRCPATH=" +#define SCMD_DSTPATH "DSTPATH=" +#define SCMD_SRCFILE "SRCFILE=" +#define SCMD_DSTFILE "DSTFILE=" +#define SCMD_ASCIIMODE 0 +#define SCMD_BINMODE 1 + +/* Definitions for maxims and defaults etc. +*/ +#define DEF_RETURN_BUF_SIZE 2048 +#define DEF_RETURN_MODE SCMD_BINMODE +#define MAX_MODE_SIZE 6 +#define MAX_FILENAME_LEN 128 +#define MIN_RETURN_BUF_SIZE 1 +#define MAX_RETURN_BUF_SIZE 65536 + +/* Structure for variables needed within this module. +*/ +typedef struct { + UINT nRetBufSize; /* Return buffer transmit size */ + UINT nRetMode; /* Return Transmit mode 1=B, 0=A */ +} SCMD_DRIVER; + +/* Allocate global variables by instantiating the above structure. +*/ +static SCMD_DRIVER SCMD; + +/* Prototypes of internal functions, not seen by any outside module. +*/ +int _SCMD_GetStrArg( UCHAR *, int, UCHAR *, UCHAR ** ); +int _SCMD_ValidatePath( UCHAR * ); +int _SCMD_ValidateFile( UCHAR *, UCHAR *, UINT ); +int _SCMD_ValidateTime( UCHAR *, ULNG * ); +#if defined(SOLARIS) || defined(SUNOS) +int _SCMD_Exec(int, UCHAR *, UCHAR *, UCHAR *, ULNG, int(*)(UCHAR *, UINT), UCHAR * ); +#endif + +#endif /* SDD_SCMD_H */ diff --git a/SDD/sdd_sybc.c b/SDD/sdd_sybc.c new file mode 100755 index 0000000..37ee132 --- /dev/null +++ b/SDD/sdd_sybc.c @@ -0,0 +1,1204 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_sybc.c + * Description: Server Data-source Driver library driver to handle sybase + * connectivity. + * + * 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 + +/* Indicate that we are a C module for any header specifics. +*/ +#define SDD_SYBC_C + +/* Bring in local specific header files. +*/ +#include "sdd.h" +#include +#include +#include "sdd_sybc.h" + +/****************************************************************************** + * Function: _SYBC_GetArg + * Description: Function to scan an input buffer and extract a required + * argument from it. + * + * Returns: SDD_FAIL- Couldnt obtain argument. + * SDD_OK - Argument obtained and validated. + ******************************************************************************/ +int _SYBC_GetArg( UCHAR *szArgType, /* I: Type of Arg to scan for */ + UCHAR *snzDataBuf, /* I: Input buffer */ + int nDataLen, /* I: Len of data */ + UCHAR **pszArg ) /* O: Pointer to Arg */ +{ + /* Local variables. + */ + int nCmpLen; + int nPos; + int nReturn = SDD_FAIL; + UCHAR *szFunc = "_SYBC_GetArg"; + + /* Go through the input buffer looking for a 'szArgType' within it. If it + * exists, then note the position just after it as this contains the + * database name. + */ + for(nPos=0, nCmpLen=strlen(szArgType); nPos < nDataLen; nPos++) + { + /* If a match occurs, then setup the pointer to correct location. + */ + if(strncmp(&snzDataBuf[nPos], szArgType, nCmpLen) == 0) + { + /* Make sure that the match is not a sub-match as some of the + * variables we are scanning for are similar. + */ + if( (nPos == 0) || + (nPos > 0 && snzDataBuf[nPos-1] == '\0') || + (nPos > 0 && isspace(snzDataBuf[nPos-1])) ) + { + nPos += nCmpLen; + break; + } + } + } + + /* If the pointer did not reach the end of the buffer then we have + * located a valid name. + */ + if(nPos < nDataLen) + { + /* Setup the callers pointer to point to the correct location + * in the buffer. + */ + *pszArg = &snzDataBuf[nPos]; + nReturn=SDD_OK; + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SYBC_RunSql + * Description: Function to execute a given buffer of SQL on the current + * database and return resultant data to the original caller. + * + * Returns: SDD_FAIL- SQL execution failed, see error message. + * SDD_OK - SQL execution succeeded. + ******************************************************************************/ +int _SYBC_RunSql( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + DBINT lResultBufLen; + UINT nFirstRow; + UCHAR *pszTmpBuf; + UCHAR *szFunc = "_SYBC_RunSql"; + + /* Need to NULL terminate the buffer prior to executing it, so allocate + * some memory and copy the original buffer into it. + */ + if((pszTmpBuf=(UCHAR *)malloc(nDataLen+1)) == NULL) + { + sprintf(szErrMsg, + "%s: Out of memory trying to exec SQL buffer", + SDD_EMSG_MEMORY); + return(SDD_FAIL); + } + memcpy(pszTmpBuf, snzDataBuf, nDataLen); + pszTmpBuf[nDataLen] = '\0'; + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "SQL: %s", pszTmpBuf); + + /* Load SQL into sybase's SQL buffer. + */ + if(dbcmd(SYB.dbProc, pszTmpBuf) == FAIL) + { + /* Free up memory as it can no longer be used. + */ + free(pszTmpBuf); + sprintf(szErrMsg, + "%s: Couldnt load SQL into sybase exec buffer", + SDD_EMSG_SQLLOAD); + return(SDD_FAIL); + } + + /* Free up memory, no longer needed. + */ + free(pszTmpBuf); + + /* Execute the SQL on the server, paying attention to the results. + */ + if(dbsqlexec(SYB.dbProc) == FAIL) + { + sprintf(szErrMsg, + "%s: Couldnt execute SQL due to syntax errors", + SDD_EMSG_SQLSYNTAX); + return(SDD_FAIL); + } + + /* Initiate sybase into the return-us-results mode, trapping any errors. + */ + if(dbresults(SYB.dbProc) == FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build an error message and exit. + */ + sprintf(szErrMsg, + "%s: Runtime error during SQL execution", + SDD_EMSG_SQLRUNERR); + return(SDD_FAIL); + } + + /* Initialise any required variables. + */ + SYB.nAbortPending = FALSE; + + /* Loop until we have exhausted the number of rows or an ABORT condition + * occurs. + */ + do { + /* Initialise any loop variables. + */ + nFirstRow = TRUE; + + /* Work out the maximum size of a memory buffer we need to hold returned + * results. + */ + lResultBufLen=dbspr1rowlen(SYB.dbProc); + + /* Allocate memory to hold returned results. + */ + if((pszTmpBuf=(UCHAR *)malloc(lResultBufLen+1)) == NULL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Out of memory trying to alloc result buffer", + SDD_EMSG_MEMORY); + return(SDD_FAIL); + } + + do { + /* If this is the first row then get the column header, else get + * the actual rows. + */ + if( nFirstRow == TRUE ) + { + /* Fetch a list of all the columns we are returning. + */ + if(dbsprhead(SYB.dbProc, pszTmpBuf, lResultBufLen) == FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Free up memory used. + */ + free(pszTmpBuf); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Error in fetching row header from server", + SDD_EMSG_FETCHHEAD); + return(SDD_FAIL); + } + + /* Update flag so we dont do this again. + */ + nFirstRow = FALSE; + } else + { + /* Fetch one row of data from the server. + */ + if(dbspr1row(SYB.dbProc, pszTmpBuf, lResultBufLen) == FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Free up memory used. + */ + free(pszTmpBuf); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Error in fetching a result row from server", + SDD_EMSG_FETCHROW); + return(SDD_FAIL); + } + } + + /* Log row if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, "%s", pszTmpBuf); + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(pszTmpBuf, strlen(pszTmpBuf)) == SDD_FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Free up memory used. + */ + free(pszTmpBuf); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + return(SDD_FAIL); + } + } while( (SYB.nAbortPending == FALSE) && + (dbnextrow(SYB.dbProc) != NO_MORE_ROWS) ); + + /* Free up memory used. + */ + free(pszTmpBuf); + } while(SYB.nAbortPending == FALSE && + dbresults(SYB.dbProc) != NO_MORE_RESULTS); + + /* If an abort command arrived halfway through processing then tidy up + * and exit. + */ + if(SYB.nAbortPending == TRUE) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: ABORT received, query abandoned", + SDD_EMSG_ABORTRCVD); + return(SDD_FAIL); + } + + /* Return success to caller as everything worked. + */ + return(SDD_OK); +} + +/****************************************************************************** + * Function: sybc_InitService + * Description: Entry point which initialises the driver into a defined state. + * It is mandatory that this function is called before any other + * in order for the driver to function correctly. The caller + * provides it with two types of data, 1) A structure containing + * data for it to use in initialising itself, 2) a pointer to a + * buffer which the driver uses to place an error message should + * it not be able to complete initialisation. + * + * Returns: SDD_FAIL- An error occurred in initialising the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver initialised successfully. + ******************************************************************************/ +int sybc_InitService( SERVICEDETAILS *sServiceDet, /* I: Init data */ + UCHAR *szErrStr ) /* O: Error message */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "sybc_InitService"; + + /* Copy all configuration data out of the service structure. + */ + strcpy(SYB.szUserName, sServiceDet->uServiceInfo.sSybaseInfo.szUser); + strcpy(SYB.szPassword, sServiceDet->uServiceInfo.sSybaseInfo.szPassword); + strcpy(SYB.szServer, sServiceDet->uServiceInfo.sSybaseInfo.szServer); + strcpy(SYB.szDatabase, sServiceDet->uServiceInfo.sSybaseInfo.szDatabase); + + /* Initialise the sybase db library. + */ + if( dbinit() == FAIL ) + { + sprintf(szErrStr, + "%s: Couldnt initialise DBLIBRARY", + SDD_EMSG_SRCINIT); + return(SDD_FAIL); + } + + /* Initialise the login maintence record. + */ + if( (SYB.sLoginRec=dblogin()) == (LOGINREC *)NULL ) + { + sprintf(szErrStr, + "%s: Couldnt initialise Login Maintenance Record", + SDD_EMSG_SRCINIT); + return(SDD_FAIL); + } + + /* Setup options and user id info within the login record. + */ + DBSETLUSER(SYB.sLoginRec, SYB.szUserName); + DBSETLPWD(SYB.sLoginRec, SYB.szPassword); + DBSETLAPP(SYB.sLoginRec, DEF_APPNAME); + + /* Now perform the actual login to the server. + */ + SYB.dbProc = dbopen(SYB.sLoginRec, SYB.szServer); + if( SYB.dbProc == (DBPROCESS *)NULL ) + { + sprintf(szErrStr, + "%s: Couldnt login to named data server (%s)", + SDD_EMSG_BADLOGIN, SYB.szServer); + return(SDD_FAIL); + } + + /* Setup options on the database specific for this modules usage. + */ + dbsetopt(SYB.dbProc, DBPRCOLSEP, DEF_COLSEP, 1); + dbsetopt(SYB.dbProc, DBPRLINELEN, "1024", 4); + dbsetopt(SYB.dbProc, DBPRPAD, DEF_PADCHR, DBPADON); + + /* OK, server connection is valid, so lets connect to the required db. + */ + if( dbuse(SYB.dbProc, SYB.szDatabase) == FAIL ) + { + sprintf(szErrStr, + "%s: Couldnt open named database (%s)", + SDD_EMSG_BADDBASE, SYB.szDatabase); + return(SDD_FAIL); + } + + /* Soak up any/all init results. + */ + while(dbresults(SYB.dbProc) != NO_MORE_RESULTS) + { + while(dbnextrow(SYB.dbProc) != NO_MORE_ROWS); + } + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, + "Sybase Driver: Initialised: (User=%s, Pwd=, Srv=%s, DB=%s)", + SYB.szUserName, SYB.szServer, SYB.szDatabase); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SYBC_ListDB + * Description: Function to list all the names of databases available on the + * currently open data source. + * + * Returns: SDD_FAIL- SQL execution failed, see error message. + * SDD_OK - SQL execution succeeded. + ******************************************************************************/ +int _SYBC_ListDB( int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + ULNG lResultBufLen; + UCHAR szDbName[MAX_SYBC_DBNAME]; + UCHAR *pszTmpBuf; + UCHAR *szFunc = "_SYBC_ListDB"; + + /* Load SQL into sybase's SQL buffer which requests a list of all known + * databases. + */ + if(dbcmd(SYB.dbProc, "sp_databases") == FAIL) + { + /* Build error message to indicate problem. + */ + sprintf(szErrMsg, + "%s: Couldnt issue request to Sybase", SDD_EMSG_SQLLOAD); + return(SDD_FAIL); + } + + /* Execute the SQL on the server, paying attention to the results. + */ + if(dbsqlexec(SYB.dbProc) == FAIL) + { + sprintf(szErrMsg, + "%s: Issued request to sybase generated syntax errors", + SDD_EMSG_SQLSYNTAX); + return(SDD_FAIL); + } + + /* Initiate sybase into the return-us-results mode, trapping any errors. + */ + if(dbresults(SYB.dbProc) == FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build an error message and exit. + */ + sprintf(szErrMsg, "%s: Runtime error during request execution", + SDD_EMSG_SQLRUNERR); + return(SDD_FAIL); + } + + /* Initialise any required variables. + */ + SYB.nAbortPending = FALSE; + + /* Work out the maximum size of a memory buffer we need to hold returned + * results. + */ + lResultBufLen=dbspr1rowlen(SYB.dbProc); + + /* Allocate memory to hold returned results. + */ + if((pszTmpBuf=(UCHAR *)malloc(lResultBufLen+1)) == NULL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Out of memory trying to alloc result buffer", + SDD_EMSG_MEMORY); + return(SDD_FAIL); + } + + /* Loop until we have exhausted the number of result sets or an ABORT + * condition occurs. + */ + do { + /* Bind to relevant return columns. + */ + dbbind(SYB.dbProc, 1, NTBSTRINGBIND, (DBINT)0, szDbName); + + /* Loop to extract all rows returned. + */ + while( (SYB.nAbortPending == FALSE) && + (dbnextrow(SYB.dbProc) != NO_MORE_ROWS) ) + { + /* Build up output line. + */ + sprintf(pszTmpBuf, "%s", szDbName); + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(pszTmpBuf, strlen(pszTmpBuf)) == SDD_FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Free up memory used. + */ + free(pszTmpBuf); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + return(SDD_FAIL); + } + } + } while(SYB.nAbortPending == FALSE && + dbresults(SYB.dbProc) != NO_MORE_RESULTS); + + /* Free up memory used. + */ + free(pszTmpBuf); + + /* If an abort command arrived halfway through processing then tidy up + * and exit. + */ + if(SYB.nAbortPending == TRUE) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: ABORT received, query abandoned", + SDD_EMSG_ABORTRCVD); + return(SDD_FAIL); + } + + /* Return success/fail to caller depending on execution results. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SYBC_ListTables + * Description: Function to list all names of tables in a given database + * (or current database if no database name given). + * + * Returns: SDD_FAIL- SQL execution failed, see error message. + * SDD_OK - SQL execution succeeded. + ******************************************************************************/ +int _SYBC_ListTables( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *pszDbName; + UCHAR szTableName[MAX_SYBC_TABLENAME]; + UCHAR szTableType[MAX_SYBC_TABLENAME]; + ULNG lResultBufLen; + UCHAR *pszTmpBuf; + UCHAR *szFunc = "_SYBC_ListTables"; + + /* See if the caller has provided the name of a new database. If HE hasnt + * then assume current database. + */ + if(_SYBC_GetArg(SYBC_DBNAME, snzDataBuf, nDataLen, &pszDbName) == SDD_FAIL) + { + /* If the length of the input buffer indicates that there is data within + * it, yet it does not contain a database name, then it is illegal. + */ + if(nDataLen > 1) + { + sprintf(szErrMsg, "%s: Invalid DATABASE NAME provided", + SDD_EMSG_BADCMD); + return(SDD_FAIL); + } else + { + /* Setup the variable so that the select below will work on the + * current database. + */ + pszDbName = NULL; + } + } else + { + /* AAAAAHHHHH! Database name switching not yet supported... tee + * hee, message for the sucker!! + */ + sprintf(szErrMsg, + "%s: Sybase switch to database function not yet implemented", + SDD_EMSG_NOTYI); + return(SDD_FAIL); + } + + /* Load SQL into sybase's SQL buffer which requests a list of all known + * tables. + */ + if(dbcmd(SYB.dbProc, "sp_tables") == FAIL) + { + /* Build error message to indicate problem. + */ + sprintf(szErrMsg, + "%s: Couldnt issue request to Sybase", SDD_EMSG_SQLLOAD); + return(SDD_FAIL); + } + + /* Execute the SQL on the server, paying attention to the results. + */ + if(dbsqlexec(SYB.dbProc) == FAIL) + { + sprintf(szErrMsg, + "%s: Issued request to sybase generated syntax errors", + SDD_EMSG_SQLSYNTAX); + return(SDD_FAIL); + } + + /* Initiate sybase into the return-us-results mode, trapping any errors. + */ + if(dbresults(SYB.dbProc) == FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build an error message and exit. + */ + sprintf(szErrMsg, "%s: Runtime error during request execution", + SDD_EMSG_SQLRUNERR); + return(SDD_FAIL); + } + + /* Initialise any required variables. + */ + SYB.nAbortPending = FALSE; + + /* Work out the maximum size of a memory buffer we need to hold returned + * results. + */ + lResultBufLen=dbspr1rowlen(SYB.dbProc); + + /* Allocate memory to hold returned results. + */ + if((pszTmpBuf=(UCHAR *)malloc(lResultBufLen+1)) == NULL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Out of memory trying to alloc result buffer", + SDD_EMSG_MEMORY); + return(SDD_FAIL); + } + + /* Loop until we have exhausted the number of result sets or an ABORT + * condition occurs. + */ + do { + /* Bind to relevant return columns. + */ + dbbind(SYB.dbProc, 3, NTBSTRINGBIND, (DBINT)0, szTableName); + dbbind(SYB.dbProc, 4, NTBSTRINGBIND, (DBINT)0, szTableType); + + /* Loop to extract all rows returned. + */ + while( (SYB.nAbortPending == FALSE) && + (dbnextrow(SYB.dbProc) != NO_MORE_ROWS) ) + { + /* Build up output line. + */ + sprintf(pszTmpBuf, "%s%s%s", szTableName, DEF_COLSEP, szTableType); + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(pszTmpBuf, strlen(pszTmpBuf)) == SDD_FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Free up memory used. + */ + free(pszTmpBuf); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + return(SDD_FAIL); + } + } + } while(SYB.nAbortPending == FALSE && + dbresults(SYB.dbProc) != NO_MORE_RESULTS); + + /* Free up memory used. + */ + free(pszTmpBuf); + + /* If an abort command arrived halfway through processing then tidy up + * and exit. + */ + if(SYB.nAbortPending == TRUE) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: ABORT received, query abandoned", + SDD_EMSG_ABORTRCVD); + return(SDD_FAIL); + } + + /* Return success/fail to caller depending on execution results. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SYBC_ListCols + * Description: Function to list all names and attributes of columns in a + * given table in a given database (or current database/table if + * no database name given). + * + * Returns: SDD_FAIL- SQL execution failed, see error message. + * SDD_OK - SQL execution succeeded. + ******************************************************************************/ +int _SYBC_ListCols( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nNullable; + int nRadix; + int nScale; + int nReturn = SDD_OK; + UINT nRowCount = 0; + long lLength; + long lPrecision; + ULNG lResultBufLen; + UCHAR szColumnName[MAX_SYBC_COLNAME]; + UCHAR szTypeName[MAX_SYBC_TYPENAME]; + UCHAR *pszColFilter; + UCHAR *pszDbName; + UCHAR *pszTableName; + UCHAR *pszTmpBuf; + UCHAR *szFunc = "_SYBC_ListCols"; + + /* See if the caller has provided the name of a new database. If HE hasnt + * then assume current database. + */ + if(_SYBC_GetArg(SYBC_DBNAME, snzDataBuf, nDataLen, &pszDbName) == SDD_FAIL) + { + /* Setup the variable so that the select below will work on the + * current database. + */ + pszDbName = NULL; + } else + { + /* AAAAAHHHHH! Database name switching not yet supported... tee + * hee, message for the sucker!! + */ + sprintf(szErrMsg, + "%s: Sybase switch to database function not yet implemented", + SDD_EMSG_NOTYI); + return(SDD_FAIL); + } + + /* Caller must provide the name of a table... else what do we scan for... + * a French Empire, vapourware... tell me more!! + */ + if(_SYBC_GetArg(SYBC_TABLENAME, snzDataBuf, nDataLen, &pszTableName) + == SDD_FAIL) + { + /* Always generate an error... French Empire.. bah humbug! + */ + sprintf(szErrMsg, "%s: Invalid TABLE NAME provided", + SDD_EMSG_BADARGS); + return(SDD_FAIL); + } + + /* See if the caller has provided the name of a specific column. If HE has + * then he requires data limited to just that one column. + */ + if(_SYBC_GetArg(SYBC_COLFILTER,snzDataBuf,nDataLen,&pszColFilter)==SDD_FAIL) + { + /* Setup the variable so that the column filter logic below wont work + * as no specific column name has been provided, hence the caller + * wants all columns. + */ + pszColFilter = NULL; + } + + /* Load SQL into sybase's SQL buffer which requests a list of all known + * columns for a given table. + */ + if(dbfcmd(SYB.dbProc, "sp_columns %s", pszTableName) == FAIL) + { + /* Build error message to indicate problem. + */ + sprintf(szErrMsg, + "%s: Couldnt issue request to list columns for %s", + SDD_EMSG_SQLLOAD, pszTableName); + return(SDD_FAIL); + } + + /* Execute the SQL on the server, paying attention to the results. + */ + if(dbsqlexec(SYB.dbProc) == FAIL) + { + sprintf(szErrMsg, + "%s: Issued request to sybase generated syntax errors", + SDD_EMSG_SQLSYNTAX); + return(SDD_FAIL); + } + + /* Initiate sybase into the return-us-results mode, trapping any errors. + */ + if(dbresults(SYB.dbProc) == FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build an error message and exit. + */ + sprintf(szErrMsg, "%s: Runtime error during request execution", + SDD_EMSG_SQLRUNERR); + return(SDD_FAIL); + } + + /* Initialise any required variables. + */ + SYB.nAbortPending = FALSE; + + /* Work out the maximum size of a memory buffer we need to hold returned + * results. + */ + lResultBufLen=dbspr1rowlen(SYB.dbProc); + + /* Allocate memory to hold returned results. + */ + if((pszTmpBuf=(UCHAR *)malloc(lResultBufLen+1)) == NULL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Out of memory trying to alloc result buffer", + SDD_EMSG_MEMORY); + return(SDD_FAIL); + } + + /* Loop until we have exhausted the number of result sets or an ABORT + * condition occurs. + */ + do { + /* Bind to relevant return columns. + */ + dbbind(SYB.dbProc, 4, NTBSTRINGBIND, (DBINT)0, szColumnName); + dbbind(SYB.dbProc, 6, NTBSTRINGBIND, (DBINT)0, szTypeName); + dbbind(SYB.dbProc, 7, INTBIND, (DBINT)0, (BYTE *)&lPrecision); + dbbind(SYB.dbProc, 8, INTBIND, (DBINT)0, (BYTE *)&lLength); + dbbind(SYB.dbProc, 9, INTBIND, (DBINT)0, (BYTE *)&nScale); + dbbind(SYB.dbProc, 10, INTBIND, (DBINT)0, (BYTE *)&nRadix); + dbbind(SYB.dbProc, 11, INTBIND, (DBINT)0, (BYTE *)&nNullable); + + /* Loop to extract rows returned. + */ + while( (SYB.nAbortPending == FALSE) && + (dbnextrow(SYB.dbProc) != NO_MORE_ROWS) ) + { + /* If there is any data to be transmitted the run through the + * column filter if applicable and transmit it to the client. + */ + if( (pszColFilter != NULL && strcmp(pszColFilter, szColumnName)==0) + || + (pszColFilter == NULL) ) + { + /* Place the appropriate data in the return buffer. + */ + sprintf(pszTmpBuf, + "%s%s%s%s%ld%s%ld%s%d%s%d%s%d", + szColumnName, DEF_COLSEP, + szTypeName, DEF_COLSEP, + lPrecision, DEF_COLSEP, + lLength, DEF_COLSEP, + nScale, DEF_COLSEP, + nRadix, DEF_COLSEP, + nNullable); + + /* Call function to transmit row to original caller. + */ + if(fSendDataCB(pszTmpBuf, strlen(pszTmpBuf)) == SDD_FAIL) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Free up memory used. + */ + free(pszTmpBuf); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: Failed to send row back to client", + SDD_EMSG_SENDROW); + return(SDD_FAIL); + } else + { + /* Increment row counter to indicate number of rows + * returned. + */ + nRowCount++; + } + } + } + } while(SYB.nAbortPending == FALSE && + dbresults(SYB.dbProc) != NO_MORE_RESULTS); + + /* Free up memory used. + */ + free(pszTmpBuf); + + /* If no rows where detected or returned then there was an error with + * the column, table or database name. + */ + if(nRowCount == 0) + { + /* Build error message for return. + */ + sprintf(szErrMsg, + (pszColFilter == NULL ? "%s: Table name not known by system" : + "%s: Column name not valid for table"), + SDD_EMSG_SQLRUNERR); + + /* Setting error code to indicate condition. + */ + nReturn = SDD_FAIL; + } + + /* If an abort command arrived halfway through processing then tidy up + * for exit. + */ + if(SYB.nAbortPending == TRUE) + { + /* Cancel the query to avoid future results-pending errors. + */ + dbcancel(SYB.dbProc); + + /* Build error message for return. + */ + sprintf(szErrMsg, + "%s: ABORT received, query abandoned", + SDD_EMSG_ABORTRCVD); + nReturn = SDD_FAIL; + } + + /* Return success/fail to caller depending on execution results. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: sybc_CloseService + * Description: Entry point which performs a drive closedown. The closedown + * procedure ensure that the driver returns to a virgin state + * (ie.like at power up) so that InitService can be called again. + * + * Returns: SDD_FAIL- An error occurred in closing the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver successfully closed. + ******************************************************************************/ +int sybc_CloseService( UCHAR *szErrMsg ) /* O: Error message if failed */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "sybc_CloseService"; + + /* Shutdown any dbase connection. + */ + dbexit(); + + /* Tidy up variables. + */ + SYB.nAbortPending = FALSE; + strcpy(SYB.szUserName, ""); + strcpy(SYB.szPassword, ""); + strcpy(SYB.szServer, ""); + strcpy(SYB.szDatabase, ""); + + /* Log if debugging switched on. + */ + Lgr(LOG_MESSAGE, szFunc, "Sybase Driver: Closed."); + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: sybc_ProcessRequest + * Description: Entry point into driver to initiate the driver into + * processing a request. A data block is passed as a parameter + * to the driver which represents a request with relevant + * parameters. The data within the structure is only relevant + * to the original client and this driver code. + * + * Returns: SDD_FAIL- An error occurred within the driver whilst trying to + * process the request, see error text. + * SDD_OK - Request processed successfully. + ******************************************************************************/ +int sybc_ProcessRequest( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(UCHAR *, UINT), + /* I: CB to send reply */ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + UCHAR *szFunc = "sybc_ProcessRequest"; + + /* If the request block doesnt contain any data, something went wrong + * somewhere?? + */ + if(nDataLen < 1) + { + sprintf(szErrMsg, + "%s: Illegal request, has size of %dBytes", + SDD_EMSG_BADREQ, nDataLen); + return(SDD_FAIL); + } + + /* First byte of the request data block indicates actions required, so + * decipher it. + */ + switch(snzDataBuf[0]) + { + /* Request to execute a block of SQL code. SQL immediately follows the + * command byte in the input buffer. + */ + case SDD_RUNSQL: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "Sybase Driver: Requested to execute SQL"); + + /* Execute the SQL. + */ + nReturn=_SYBC_RunSql(&snzDataBuf[1],nDataLen,fSendDataCB,szErrMsg); + break; + + /* Request to list all the names of the databases on the current open + * data source. + */ + case SDD_LIST_DB: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "Sybase Driver: Requested to list all Databases on source"); + + /* Call function to extract names of all databases in data source. + */ + nReturn=_SYBC_ListDB(fSendDataCB, szErrMsg); + break; + + /* Request to list all the table names of a given database on the + * current open data source. + */ + case SDD_LIST_TABLES: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "Sybase Driver: Requested to list all columns on a table"); + + /* Call function to extract names of all tables in a database. + */ + nReturn=_SYBC_ListTables(&snzDataBuf[1], nDataLen, fSendDataCB, + szErrMsg); + break; + + /* Request to list all the column names and their attributes of a + * given database/table. + */ + case SDD_LIST_COLS: + /* Log if debugging switched on. + */ + Lgr(LOG_DEBUG, szFunc, + "Sybase Driver: Requested to list all tables in a database"); + + /* Call function to extract details of all columns in a table. + */ + nReturn=_SYBC_ListCols(&snzDataBuf[1], nDataLen, fSendDataCB, + szErrMsg); + break; + + default: + sprintf(szErrMsg, + "%s: Illegal command in request buffer (%x)", + SDD_EMSG_BADCMD, snzDataBuf[0]); + return(SDD_FAIL); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: sybc_ProcessOOB + * Description: Entry point into driver to process an out of band command + * that may or may not be relevant to current state of + * operation. The task of this function is to decipher the + * command and act on it immediately, ie. a cancel command + * would abort any ProcessRequest that is in process and + * clean up. + * + * Returns: No returns. + ******************************************************************************/ +void sybc_ProcessOOB( UCHAR nCommand ) /* I: OOB Command */ +{ + /* Local variables. + */ + + /* Decipher command and perform actions accordingly. + */ + switch(nCommand) + { + /* Request to abort current ProcessRequest command and return daemon + * to a waiting-for-request state. + */ + case SDD_ABORT: + SYB.nAbortPending = TRUE; + break; + + /* Request to close down and exit. + */ + case SDD_EXIT: + break; + + default: + break; + } + + /* Return to caller. + */ + return; +} diff --git a/SDD/sdd_sybc.h b/SDD/sdd_sybc.h new file mode 100755 index 0000000..f368820 --- /dev/null +++ b/SDD/sdd_sybc.h @@ -0,0 +1,85 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_sybc.h + * Description: Server Data-source Driver library driver header file for + * sybc driver. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef SDD_SYBC_H +#define SDD_SYBC_H + +/* Definitions for maxims, defaults etc. +*/ +#define DEF_APPNAME "sdd_sybc" +#define DEF_LINELEN 1024 +#define MAX_SYBC_COLNAME 256 +#define MAX_SYBC_DBNAME 256 +#define MAX_SYBC_TABLENAME 256 +#define MAX_SYBC_TYPENAME 256 + +/* Definitions for constants, configurable options etc. +*/ +#define SYBC_COLFILTER "COLUMN=" +#define SYBC_DBNAME "DBNAME=" +#define SYBC_TABLENAME "TABLENAME=" + +/* Structure for variables internal to this driver module. +*/ +typedef struct { + UINT nAbortPending; /* An abort is pending */ + UCHAR szUserName[MAX_USERNAMELEN]; /* Name of user that this */ + /* module will use when */ + /* connecting to data source. */ + UCHAR szPassword[MAX_PASSWORDLEN]; /* Password that this module */ + /* will use when connecting */ + /* to data source. */ + UCHAR szServer[MAX_SERVERNAMELEN]; /* Name of Data Source server */ + UCHAR szDatabase[MAX_DBNAMELEN]; /* Name of Data Source data */ + /* base. */ + DBPROCESS *dbProc; /* Process handle into sybase */ + LOGINREC *sLoginRec; /* Login record for making */ + /* and maintaining connection */ + /* with sybase */ +} SYB_DRIVER; + +/* Allocate any global variables needed within this module. +*/ +static SYB_DRIVER SYB; + +/* Prototypes of internal functions, not seen by any outside module. +*/ +int _SYBC_GetArg( UCHAR *, UCHAR *, int, UCHAR ** ); +int _SYBC_RunSql( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +int _SYBC_ListDB( int (*)(UCHAR *, UINT), UCHAR * ); +int _SYBC_ListTables( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); +int _SYBC_ListCols( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); + +#endif /* SDD_SYBC_H */ diff --git a/SDD/templates/sdd_xxxx.c b/SDD/templates/sdd_xxxx.c new file mode 100755 index 0000000..de596ed --- /dev/null +++ b/SDD/templates/sdd_xxxx.c @@ -0,0 +1,157 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_xxxx.c + * Description: Server Data-source Driver library driver to handle xxxx + * connectivity. + * + * 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 + +/* Indicate that we are a C module for any header specifics. +*/ +#define SDD_xxxx_C + +/* Bring in local specific header files. +*/ +#include "sdd.h" +#include "sdd_xxxx.h" + +/****************************************************************************** + * Function: xxxx_InitService + * Description: Entry point which initialises the driver into a defined state. + * It is mandatory that this function is called before any other + * in order for the driver to function correctly. The caller + * provides it with two types of data, 1) A structure containing + * data for it to use in initialising itself, 2) a pointer to a + * buffer which the driver uses to place an error message should + * it not be able to complete initialisation. + * + * Returns: SDD_FAIL- An error occurred in initialising the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver initialised successfully. + ******************************************************************************/ +int xxxx_InitService( SERVICEDETAILS *sServiceDet, /* I: Init data */ + UCHAR *szErrStr ) /* O: Error message */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: xxxx_CloseService + * Description: Entry point which performs a drive closedown. The closedown + * procedure ensure that the driver returns to a virgin state + * (ie.like at power up) so that InitService can be called again. + * + * Returns: SDD_FAIL- An error occurred in closing the driver and an + * error message is stored in szErrStr. + * SDD_OK - Driver successfully closed. + ******************************************************************************/ +int xxxx_CloseService( UCHAR *szErrMsg ) /* O: Error message if failed */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: xxxx_ProcessRequest + * Description: Entry point into driver to initiate the driver into + * processing a request. A data block is passed as a parameter + * to the driver which represents a request with relevant + * parameters. The data within the structure is only relevant + * to the original client and this driver code. + * + * Returns: SDD_FAIL- An error occurred within the driver whilst trying to + * process the request, see error text. + * SDD_OK - Request processed successfully. + ******************************************************************************/ +int xxxx_ProcessRequest( UCHAR *snzDataBuf, /* I: Input data */ + int nDataLen, /* I: Len of data */ + int (*fSendDataCB)(), /* I: CB to send reply*/ + UCHAR *szErrMsg ) /* O: Error text */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: xxxx_ProcessOOB + * Description: Entry point into driver to process an out of band command + * that may or may not be relevant to current state of + * operation. The task of this function is to decipher the + * command and act on it immediately, ie. a cancel command + * would abort any ProcessRequest that is in process and + * clean up. + * + * Returns: No returns. + ******************************************************************************/ +void xxxx_ProcessOOB( UINT nCommand ) /* I: OOB Command */ +{ + /* Local variables. + */ + + /* Decipher command and perform actions accordingly. + */ + switch(nCommand) + { + /* Request to abort current ProcessRequest command and return daemon + * to a waiting-for-request state. + */ + case SDD_ABORT: + break; + + default: + break; + } + + /* Return to caller. + */ + return; +} diff --git a/SDD/templates/sdd_xxxx.h b/SDD/templates/sdd_xxxx.h new file mode 100755 index 0000000..5ba5001 --- /dev/null +++ b/SDD/templates/sdd_xxxx.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * Product: ##### ###### ###### # ### ###### + * # # # # # # # # # # + * # # # # # # # # # + * ##### # # # # # # ###### + * # # # # # # # # # + * # # # # # # # # # # + * ##### ###### ###### ####### ####### ### ###### + * + * File: sdd_xxxx.h + * Description: Server Data-source Driver library driver header file for + * xxxx driver. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef SDD_xxxx_H +#define SDD_xxxx_H + +/* Definitions for maxims etc. +*/ + +#endif /* SDD_xxxx_H */ diff --git a/VDW/1bin/RunForLinuxClient b/VDW/1bin/RunForLinuxClient new file mode 100755 index 0000000..a5d8ce8 --- /dev/null +++ b/VDW/1bin/RunForLinuxClient @@ -0,0 +1,3 @@ +#! /bin/csh + +/dvlp/VDW/1bin/vdwd -m 2 -l /dvlp/VDW/log/`hostname`.log & diff --git a/VDW/4bin/RunForSunOSClient b/VDW/4bin/RunForSunOSClient new file mode 100755 index 0000000..cae6271 --- /dev/null +++ b/VDW/4bin/RunForSunOSClient @@ -0,0 +1,5 @@ +#! /bin/csh + +setenv LD_LIBRARY_PATH /dvlp/third/ilog/views/lib/sparc_4_4.0:/dvlp/third/contrib/SunOS:/usr/motif/lib:/usr/openwin/lib:/dvlp/odbc/dlls:/dvlp2/sybase/syb1002_SunOS4/lib:/usr/ucblib +setenv SYBASE /home/sybase +/dvlp/VDW/4bin/vdwd -m 0 > /dvlp/VDW/log/`hostname`.log & diff --git a/VDW/5bin/Run b/VDW/5bin/Run new file mode 100755 index 0000000..f420018 --- /dev/null +++ b/VDW/5bin/Run @@ -0,0 +1,4 @@ +#! /bin/csh + +setenv LD_LIBRARY_PATH /usr/motif/lib:/usr/openwin/lib:/home/psmart/dvlp/odbc/dlls:/apps/sybase/lib:/usr/ucblib +/home/psmart/dvlp/VDW/5bin/vdwd -m 3 > /tmp/`hostname`.log & diff --git a/VDW/5bin/RunForSolarisClient b/VDW/5bin/RunForSolarisClient new file mode 100755 index 0000000..59bbfdd --- /dev/null +++ b/VDW/5bin/RunForSolarisClient @@ -0,0 +1,4 @@ +#! /bin/csh + +setenv LD_LIBRARY_PATH /dvlp/third/ilog/views/lib/sparc_4_4.0:/dvlp/third/contrib/SunOS:/usr/motif/lib:/usr/openwin/lib:/dvlp/odbc/dlls:/dvlp2/sybase/syb1002_SunOS5/lib:/usr/ucblib +/dvlp/VDW/5bin/vdwd -m 0 > /dvlp/VDW/log/`hostname`.log & diff --git a/VDW/Makefile b/VDW/Makefile new file mode 100755 index 0000000..445d87d --- /dev/null +++ b/VDW/Makefile @@ -0,0 +1,154 @@ +#****************************************************************************** +#* Product: # # ###### # # +#* # # # # # # # +#* # # # # # # # +#* # # # # # # # +#* # # # # # # # +#* # # # # # # # +#* # ###### ## ## +#* +#* File: Makefile +#* Description: Build description file for the Virtual Data Warehouse +#* Daemon. +#* 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 . +#****************************************************************************** +TITLE = "Virtual Data Warehouse Daemon" +COPYRIGHT = "(C) P.D.Smart, %D%, Vers %I%" +PROJ = +PURIFY = #purify -best-effort -follow-child-processes=yes +PROJPATH = ../VDW +GNUINCLUDE = #-I/apps/gnu/$(ARCH)/include +MDCINCLUDE = -I../MDC +SDDINCLUDE = -I../SDD +UXINCLUDE = -I../ux +INCLUDEDIR = -I. $(UXINCLUDE) $(MDCINCLUDE) $(SDDINCLUDE) $(GNUINCLUDE) +1DEBUGFLAGS = -g #-DMDC_DEBUG #-E +4DEBUGFLAGS = -g #-DMDC_DEBUG #-E +5DEBUGFLAGS = -g #-DMDC_DEBUG #-E +1OPTIMIZEFLAGS = #-O2 +4OPTIMIZEFLAGS = #-O2 +5OPTIMIZEFLAGS = #-O2 +1OPTIONFLAGS = -D${OS} #-ansi -Wall +4OPTIONFLAGS = -D${OS} #-ansi -Wall +5OPTIONFLAGS = -D${OS} -D_REENTRANT #-ansi -Wall +CFLAGS = $(${OSVER}DEBUGFLAGS) $(${OSVER}OPTIMIZEFLAGS) \ + $(${OSVER}OPTIONFLAGS) +LDFLAGS = #-static +MDCLIBS = -L../MDC/${OSVER}lib -lmdc +ODBCLIBS = -L../odbc/dlls -lodbc +SDDLIBS = -L../SDD/${OSVER}lib -lsdd +1SYBLIBS = +4SYBLIBS = -L/apps/sybase/lib -lsybdb +5SYBLIBS = -L/apps/sybase/lib -lsybdb +UXLIBS = -L../ux/${OSVER}lib -lux +1LIBS = -lm +4LIBS = -lm +5LIBS = -L/usr/ucblib -lsocket -lnsl -lucb #-liberty -lucb +LIBS = $(MDCLIBS) $(SDDLIBS) $(UXLIBS) $(${OSVER}SYBLIBS) $(${OSVER}LIBS) +SCCSFLAGS = -d$(PROJPATH) +SCCSGETFLAGS = + +ifeq ($(ZPU_BUILD),) +BASE = +else +BASE = zpu-elf- +endif + +CC = $(BASE)gcc +LD = $(BASE)gcc +AS = $(BASE)as +AR = $(BASE)ar +CP = $(BASE)objcopy +DUMP = $(BASE)objdump +RANLIB = $(BASE)ranlib + +# Suffixes where interested in for this project. +# +.SUFFIXES: +.SUFFIXES: .o .c .h + +# Our way of making an object file. +# +.c.o: + $(PURIFY) $(CC) $(INCLUDEDIR) $(CFLAGS) -c $< + +# All, ie: all programs to be built +# +all: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Use 'build' command to make Virtual DataWarehouse Daemon." + +VDWD: Begin \ + vdwd \ + End + +# How to clean up the directory... make it look pretty! +# +clean: Begin \ + DoClean \ + End + +# How to perform an installation of the resultant software. +# +install: Begin \ + DoInstall \ + End + +# +# Pre-make start sequence. +# +Begin: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Operation commencing @ `date`" + @echo + +# +# Post-make completion sequence. +# +End: + @echo + @echo "Completed @ `date`" + +# Perform all cleanup operations to ensure future builds occur +# with completeness. +# +DoClean: + rm -f *.o *.bak *.a *.BAK *.sav core + +# Perform installation of software as per spec. +# +DoInstall: + +# Build the Virtual Data Warehouse Daemon Process. +# +vdwd: vdwd.o config.o + $(PURIFY) $(CC) $(LDFLAGS) -o vdwd \ + vdwd.o \ + config.o \ + $(LIBS) + +vdwd.o: vdwd.c vdwd.h + +config.o: config.c vdwd.h diff --git a/VDW/build b/VDW/build new file mode 100755 index 0000000..04f76e0 --- /dev/null +++ b/VDW/build @@ -0,0 +1,172 @@ +#!/bin/csh +#****************************************************************************** +#* Product: # # ###### # # +#* # # # # # # # +#* # # # # # # # +#* # # # # # # # +#* # # # # # # # +#* # # # # # # # +#* # ###### ## ## +#* +#* File: VDWd +#* Description: Build description file for the Virtual Data Warehouse +#* daemon. +#* 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 . +#****************************************************************************** + +# Work out architecture we are compiling on.. +# +setenv ARCH `uname -s` +setenv ARCH ${ARCH}`uname -r | cut -c1` + +# Work out arguments and decide on actions from there. +# +if( $#argv > 0 ) then + + switch("$argv[1]") + + case "save": + make -f Makefile clean + sccs delta SCCS + exit 0 + + case "install": + echo "Installing executables." + echo -n "Please enter 'vdwd' " + su - vdwd <. + ******************************************************************************/ +/* Bring in system header files. +*/ +#include +#include +#include +#include +#include +#include + +/* Indicate that we are a C module for any header specifics. +*/ +#define VDWD_CONFIG_C + +/* Bring in local specific header files. +*/ +#include "vdwd.h" + +/* Driver entry point configuration table. + * If you have a driver that you wish to embed within the daemon then enter + * it into this table. Ordering is not relevant as you must provide a driver + * type code which is known to both the client code and your driver. +*/ +VDWD_DRIVERS Driver[]={ +/* #if defined(SOLARIS) || defined(_WIN32) +# { SRV_ODBC, odbc_InitService, odbc_CloseService, +# odbc_ProcessRequest, odbc_ProcessOOB }, +#endif */ +#if defined(SOLARIS) || defined(SUNOS) || defined(_WIN32) + { SRV_SYBASE, sybc_InitService, sybc_CloseService, + sybc_ProcessRequest, sybc_ProcessOOB }, +#endif + { SRV_JAVA, java_InitService, java_CloseService, + java_ProcessRequest, java_ProcessOOB }, + { SRV_SCMD, scmd_InitService, scmd_CloseService, + scmd_ProcessRequest, scmd_ProcessOOB }, + { SRV_FTPX, ftpx_InitService, ftpx_CloseService, + ftpx_ProcessRequest, ftpx_ProcessOOB }, +/* # { SRV_AUPL, aupl_InitService, aupl_CloseService, +# aupl_ProcessRequest, aupl_ProcessOOB }, */ + { 0, NULL, NULL, + NULL, NULL } +}; diff --git a/VDW/vdwd.c b/VDW/vdwd.c new file mode 100755 index 0000000..fa86484 --- /dev/null +++ b/VDW/vdwd.c @@ -0,0 +1,726 @@ +/****************************************************************************** + * Product: # # ###### # # + * # # # # # # # ##### + * # # # # # # # # # + * # # # # # # # # # + * # # # # # # # # # + * # # # # # # # # # + * # ###### ## ## ##### + * + * File: vdwd.c + * Description: The Virtual Data Warehouse server Daemon process. A daemon + * built upon the API libraries of MDC and the Server Data-source + * Driver libraries to provide a generic mechanism for + * retrieving data from any data source local to the daemon. + * The daemon is designed to interact with VDW client + * applications and serve their data needs. + * + * 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 + +/* Specials for Solaris. +*/ +#if defined(SOLARIS) || defined(LINUX) +#include +#include +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define VDWD_C + +/* Bring in local specific header files. +*/ +#include "vdwd.h" + +/****************************************************************************** + * Function: GetConfig + * Description: Get configuration information from the OS or command line + * flags. + * + * Returns: VDWD_OK - Configuration obtained. + * VDWD_FAIL - Failure, see error message. + ******************************************************************************/ +int GetConfig( int argc, /* I: CLI argument count */ + UCHAR **argv, /* I: CLI argument contents */ + char **envp, /* I: Environment variables */ + UCHAR *szErrMsg ) /* O: Any generated error message */ +{ + /* Local variables. + */ + int nReturn = VDWD_OK; + FILE *fp; + UCHAR *szFunc = "GetConfig"; + + /* See if the user wishes to use a logfile? + */ + if( GetCLIParam(argc, argv, FLG_LOGFILE, T_STR, VDWD.szLogFile, + MAX_LOGFILELEN, FALSE) == R_OK ) + { + /* Check to see if the filename is valid. + */ + if((fp=fopen(VDWD.szLogFile, "a")) == NULL) + { + sprintf(szErrMsg, "Cannot write to logfile (%s)", VDWD.szLogFile); + return(VDWD_FAIL); + } + + /* Close the file as test complete. + */ + fclose(fp); + } else + { + /* Set logfile to a default, dependant on OS. + */ + strcpy(VDWD.szLogFile, DEF_LOGFILE); + } + + /* Get log mode from command line. + */ + if(GetCLIParam(argc, argv, FLG_LOGMODE, T_INT, (UCHAR *)&VDWD.nLogMode, + 0, 0) == R_OK) + { + /* Check the validity of the mode. + */ + if((VDWD.nLogMode < LOG_OFF || VDWD.nLogMode > LOG_FATAL) && + VDWD.nLogMode != LOG_CONFIG) + { + sprintf(szErrMsg, "Illegal Logger mode (%d)", VDWD.nLogMode); + return(VDWD_FAIL); + } + } else + { + /* Setup default log mode. + */ + VDWD.nLogMode = LOG_MESSAGE; + } + + /* Finished, get out! + */ + return( nReturn ); +} + +/****************************************************************************** + * Function: VDWDInit + * Description: Initialisation of variables, functionality, communications + * and turning the process into a daemon. + * + * Returns: VDWD_OK - Initialised successfully. + * VDWD_FAIL - Failure, see error message. + ******************************************************************************/ +int VDWDInit( UCHAR *szErrMsg ) /* O: Generated error message */ +{ + /* Local variables. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + pid_t pid; +#endif + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + /* To start daemonisation, we must fork a child to detach from the + * parent process. + */ + if( (pid=fork()) < 0 ) + { + /* Couldnt fork a child, so build up an error message and then exit + * with failure code. + */ + sprintf(szErrMsg, "Couldnt fork a child process for daemonisation"); + return(VDWD_FAIL); + } else + if( pid != 0 ) + { + /* As the parent, we exit here, cela-vie. + */ + exit(0); + } + + /* OK, we are a child and are maturing to be a parent, so lets shed + * our childish skin.... + * + * Firstly, become session leader.. + */ + setsid(); + + /* Then ensure we are not hogging an NFS directory. + */ + chdir("/"); + + /* Setup the UMASK for known file creation state. + */ + umask(0); +#endif + + /* Setup logger mode. + */ + Lgr(LOG_CONFIG, LGM_FLATFILE, VDWD.nLogMode, VDWD.szLogFile); + + /* All done, lets get out. + */ + return(VDWD_OK); +} + +/****************************************************************************** + * Function: VDWDClose + * Description: Function to perform closure of all used resources within the + * module. + * + * Returns: VDWD_OK - Closed successfully. + * VDWD_FAIL - Failure, see error message. + ******************************************************************************/ +int VDWDClose( UCHAR *szErrMsg ) /* O: Generated error message */ +{ + /* Local variables. + */ + + /* If the service hasnt been previously initialised, then there is no + * reason to close it down!!! + */ + if(VDWD.nServiceInitialised == TRUE) + { + /* Close the currently active service + */ + if(VDWDCloseService(VDWD.nActiveService, szErrMsg) == VDWD_FAIL) + return(VDWD_FAIL); + + /* Tidy up variables. + */ + VDWD.nServiceInitialised = FALSE; + VDWD.nActiveService = 0; + } + + /* Exit with success. + */ + return(VDWD_OK); +} + +/****************************************************************************** + * Function: VDWDSentToClient + * Description: Function to send data from this daemon back to the relevant + * client. + * + * Returns: SDD_OK - Data sent successfully. + * SDD_FAIL - Failure in sending data. + ******************************************************************************/ +int VDWDSendToClient( UCHAR *snzData, /* I: Data to send */ + UINT nDataLen ) /* I: Length of data */ +{ + /* Local variables. + */ + int nReturn = SDD_OK; + + /* Call the MDC library to transmit the data. If it fails, then just + * let the driver know by the return value. + */ + if(MDC_ReturnData(snzData, nDataLen) == MDC_FAIL) + { + nReturn = SDD_FAIL; + } + + /* Return code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: VDWDInitService + * Description: Function to call a given drivers initialisation function. + * + * Returns: VDWD_OK - Service was initialised successfully. + * VDWD_FAIL - Failure, see error message. + ******************************************************************************/ +int VDWDInitService( int nServiceType, /* I: Type of service*/ + SERVICEDETAILS *sServiceDet, /* I: Service Data */ + UCHAR *szErrMsg ) /* O: Error message */ +{ + /* Local variables. + */ + UINT nNdx; + int nReturn = VDWD_OK; + UCHAR *szFunc = "VDWDInitService"; + + /* Go through list of drivers and locate one that is of the correct type. + */ + for(nNdx=0; Driver[nNdx].nType != 0 && Driver[nNdx].nType != nServiceType; + nNdx++); + + /* If we located the correct driver then we can perform initialisation. + */ + if(Driver[nNdx].nType == nServiceType) + { + /* If there is a registered Initialisation function for this driver + * then invoke it, else just return as though everything completed + * successfully. + */ + if(Driver[nNdx].InitService != NULL) + { + /* Perform the initialisation and set return code accordingly. + */ + if(Driver[nNdx].InitService(sServiceDet, szErrMsg) == SDD_FAIL) + { + nReturn = VDWD_FAIL; + } + } else + { + /* Log the fact that there is no driver. + */ + Lgr(LOG_DEBUG, szFunc, + "No registered InitService for Driver type (%d)", nServiceType); + } + } else + { + /* Build up an error message and exit. + */ + sprintf(szErrMsg, + "%s: Illegal service type (%d)", + VDWD_EMSG_BADSERVICE, nServiceType); + nReturn = VDWD_FAIL; + } + + /* Finished, return result to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: VDWDCloseService + * Description: Function to call a given drivers closedown function. + * + * Returns: VDWD_OK - Service was closed successfully. + * VDWD_FAIL - Failure, see error message. + ******************************************************************************/ +int VDWDCloseService( int nServiceType, /* I: Type of service*/ + UCHAR *szErrMsg ) /* O: Error message */ +{ + /* Local variables. + */ + UINT nNdx; + int nReturn = VDWD_OK; + UCHAR *szFunc = "VDWDCloseService"; + + /* Go through list of drivers and locate one that is of the correct type. + */ + for(nNdx=0; Driver[nNdx].nType != 0 && Driver[nNdx].nType != nServiceType; + nNdx++); + + /* If we located the correct driver then we can perform a closedown. + */ + if(Driver[nNdx].nType == nServiceType) + { + /* If there is a registered Closedown function for this driver + * then invoke it, else just return as though everything completed + * successfully. + */ + if(Driver[nNdx].CloseService != NULL) + { + /* Perform the closedown and set return code accordingly. + */ + if(Driver[nNdx].CloseService(szErrMsg) == SDD_FAIL) + { + nReturn = VDWD_FAIL; + } + } else + { + /* Log the fact that there is no driver. + */ + Lgr(LOG_DEBUG, szFunc, + "No registered CloseService for Driver type (%d)",nServiceType); + } + } else + { + /* Build up an error message and exit. + */ + sprintf(szErrMsg, + "%s: Illegal service type (%d)", + VDWD_EMSG_BADSERVICE, nServiceType); + nReturn = VDWD_FAIL; + } + + /* Finished, return result to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: VDWDProcessRequest + * Description: Function to call a given drivers function to process a + * service request. + * + * Returns: VDWD_OK - Request was processed successfully. + * VDWD_FAIL - Failure, see error message. + ******************************************************************************/ +int VDWDProcessRequest( int nServiceType, /* I: Type of service */ + UCHAR *snzData, /* I: Data Buffer */ + UINT nDataLen, /* I: Len of Data */ + UCHAR *szErrMsg ) /* O: Error message */ +{ + /* Local variables. + */ + UINT nNdx; + int nReturn = VDWD_OK; + UCHAR *szFunc = "VDWDProcessRequest"; + + /* Go through list of drivers and locate one that is of the correct type. + */ + for(nNdx=0; Driver[nNdx].nType != 0 && Driver[nNdx].nType != nServiceType; + nNdx++); + + /* If we located the correct driver then we can perform the request. + */ + if(Driver[nNdx].nType == nServiceType) + { + /* If there is a registered Process Request function for this driver + * then invoke it, else just return as though everything completed + * successfully. + */ + if(Driver[nNdx].ProcessRequest != NULL) + { + /* Call the driver function to process the request. + */ + if(Driver[nNdx].ProcessRequest(snzData, nDataLen, VDWDSendToClient, + szErrMsg) == SDD_FAIL) + { + nReturn=VDWD_FAIL; + } + } else + { + /* Log the fact that there is no driver. + */ + Lgr(LOG_DEBUG, szFunc, + "No registered ProcessRequest for Driver type (%d)", + nServiceType); + } + } else + { + /* Build up an error message and exit. + */ + sprintf(szErrMsg, + "%s: Illegal service type (%d)", + VDWD_EMSG_BADSERVICE, nServiceType); + nReturn = VDWD_FAIL; + } + + /* Finished, return result to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: VDWDDataCallback + * Description: Function which is registered as a callback and is called + * every time data arrives from a new client. + * + * Returns: MDC_OK - Closed successfully. + * MDC_FAIL - Failure, see error message. + ******************************************************************************/ +int VDWDDataCallback( UCHAR *snzData, /* I: Buffer containing data */ + int nDataLen, /* I: Length of data in buffer */ + UCHAR *szErrMsg ) /* O: Error messages generated */ +{ + /* Local variables. + */ + SERVICEDETAILS *sServiceDet; + UCHAR *szFunc = "VDWDDataCallback"; + + /* Data has arrived from a connected client. We know for sure that we + * will only ever deal with one client, so its safe to make a lot of + * assumptions about the data path. In this function, we only need to + * decipher the data packet that we've been passed and dispatch it to + * the correct handler. + */ + + /* First, check the length... it must be at least 1 byte!! + */ + if(nDataLen <= 0) + { + Lgr(LOG_DEBUG, szFunc, "Being called with %dByte packets", nDataLen); + return(MDC_FAIL); + } + + /* Work out the type of packet that we've been passed by analysing the + * first byte. + */ + switch(snzData[0]) + { + case MDC_ACK: + sprintf(szErrMsg, + "%s: Server being sent an ACK, illegal!!", + VDWD_EMSG_BADACK); + return(MDC_FAIL); + + case MDC_NAK: + sprintf(szErrMsg, + "%s: Server being sent a NAK, illegal!!", + VDWD_EMSG_BADNAK); + return(MDC_FAIL); + + case MDC_PREQ: + /* Has the service been initialised yet? If it hasnt, then we + * cant possibly pass a data block to the unknown. + */ + if(VDWD.nServiceInitialised == TRUE) + { + /* Try and process the given request. + */ + if(VDWDProcessRequest(VDWD.nActiveService, &snzData[1], + nDataLen-1, szErrMsg) == VDWD_FAIL) + { + return(MDC_FAIL); + } + } else + { + /* Just return an error message/code to indicate the problem. + */ + sprintf(szErrMsg, + "%s: No service initialised, data packet illegal!!!", + VDWD_EMSG_BADDATA); + return(MDC_FAIL); + } + break; + + case MDC_CHANGE: + case MDC_INIT: + /* Is this the first initialisation call...? If it isnt then close + * the original service prior to initialising the new service. + */ + if(VDWD.nServiceInitialised == TRUE) + { + /* Close the service, exit if we cant, using provided error + * message. + */ + if(VDWDCloseService(VDWD.nActiveService, szErrMsg) == VDWD_FAIL) + return(MDC_FAIL); + + /* Toggle the flag in case of failure. + */ + VDWD.nServiceInitialised = FALSE; + } + + /* Extract the type of service by casting the provided data to + * a service block and then using the structure types. + */ + snzData++; + sServiceDet = (SERVICEDETAILS *)snzData; + VDWD.nActiveService=(UINT)sServiceDet->cServiceType; + + /* Initialise the requested sevice. + */ + if(VDWDInitService(VDWD.nActiveService, sServiceDet, szErrMsg) + == VDWD_FAIL) + { + return(MDC_FAIL); + } + + /* Set flag to indicate that service has been initialised. + */ + VDWD.nServiceInitialised = TRUE; + break; + + default: + sprintf(szErrMsg, + "%s: Unknown Service Type (%x)", + VDWD_EMSG_BADSERVICE, snzData[0]); + return(MDC_FAIL); + } + + /* Return any result codes to MDC library. + */ + return(MDC_OK); +} + +/****************************************************************************** + * Function: VDWDOOBCallback + * Description: Function to take action on out of band commands from the + * MDC layer. Out of band messages are generally commands which + * need to be actioned upon immediately, so they are passed up + * into the Drivers out of band processing function. + * + * Returns: No returns. + ******************************************************************************/ +void VDWDOOBCallback( UCHAR cCmd ) /* I: Command to action upon */ +{ + /* Local variables. + */ + UINT nNdx; + UCHAR *szFunc = "VDWDOOBCallback"; + + /* Has a service been initialised yet? If one hasnt, then there is no + * point in going further as OOB processing is only relevant to an + * intialised module. + */ + if(VDWD.nServiceInitialised == FALSE) + { + /* Log an error message and exit. + */ + Lgr(LOG_DEBUG, szFunc, + "Services not initialised, cannot process OOB (%x)",cCmd); + return; + } + + /* Go through list of drivers and locate one that is of the correct type. + */ + for(nNdx=0; Driver[nNdx].nType != 0 && + Driver[nNdx].nType != VDWD.nActiveService; + nNdx++); + + /* If we located the correct driver then we can perform the request. + */ + if(Driver[nNdx].nType == VDWD.nActiveService) + { + /* Process out of band message according to type. + */ + switch(cCmd) + { + case MDC_ABORT: + /* Log message for debug purposes. + */ + Lgr(LOG_DEBUG, szFunc, + "ABORT Out Of Band message received, processing"); + + if(Driver[nNdx].ProcessOOB != NULL) + { + Driver[nNdx].ProcessOOB(SDD_ABORT); + } else + { + Lgr(LOG_DEBUG, szFunc, + "No registered ABORT OOB handler in driver"); + } + break; + + case MDC_EXIT: + /* Log message for debug purposes. + */ + Lgr(LOG_DEBUG, szFunc, + "EXIT Out Of Band message received, exitting"); + + if(Driver[nNdx].ProcessOOB != NULL) + { + Driver[nNdx].ProcessOOB(SDD_EXIT); + } else + { + Lgr(LOG_DEBUG, szFunc, + "No registered EXIT OOB handler in driver"); + } + break; + + default: + /* Build up an error message and exit. + */ + Lgr(LOG_DEBUG, szFunc, "Illegal command (%x)", cCmd); + break; + } + } else + { + /* Build up an error message and exit. + */ + Lgr(LOG_DEBUG, szFunc, "Called with no active service, cCmd=(%x)",cCmd); + } + + /* Return to caller. + */ + return; +} + +/****************************************************************************** + * Function: main + * Description: Entry point into the Virtual Data Warehouse Daemon. Basic + * purpose is to invoke intialisation, enter the main program + * loop and finally tidy up and close down. + * + * Returns: 0 - Program completed successfully without errors. + * -1 - Program terminated with errors, see logged message. + ******************************************************************************/ +int main( int argc, /* I: Count of available arguments */ + char **argv, /* I: Array of arguments */ + char **envp ) /* I: Array of environment parameters */ +{ + /* Local variables. + */ + UCHAR szErrMsg[MAX_ERRMSG_LEN]; + UCHAR *szFunc = "main"; + + /* Bring in any configuration parameters passed on the command line etc. + */ + if( GetConfig(argc, (UCHAR **)argv, envp, szErrMsg) == VDWD_FAIL ) + { + printf( "%s\n" + "Usage: %s \n" + ": -l\n" + " -m\n", + szErrMsg, argv[0]); + } + + /* Initialise variables, communications and become a daemon. + */ + if( VDWDInit(szErrMsg) == VDWD_FAIL ) + { + /* Log an error message to indicate reason for failure. + */ + Lgr(LOG_DIRECT, szFunc, "%s: %s", argv[0], szErrMsg); + exit(-1); + } + + /* Start the daemon running by passing control into the MDC library and + * letting it generate callbacks as events occur. + */ + if( MDC_Server(NULL, DEF_SERVICENAME, VDWDDataCallback, + VDWDOOBCallback ) == MDC_FAIL ) + { + /* Problems within the MDC library, indicate error and get out. + */ + Lgr(LOG_DIRECT, szFunc, "%s: MDC_Server error, aborting..", argv[0]); + exit(-1); + } + + /* Perform close down of used facilities ready for exit. + */ + if( VDWDClose(szErrMsg) == VDWD_FAIL ) + { + /* Log an error message to indicate reason for failure. + */ + Lgr(LOG_DIRECT, szFunc, "%s: %s", argv[0], szErrMsg); + exit(-1); + } + + /* Log off message. + */ + Lgr(LOG_DEBUG, szFunc, "Child daemon closing."); + + /* All done, go bye bye's. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) + exit; +#endif +#if defined(_WIN32) + return(0); +#endif +} diff --git a/VDW/vdwd.h b/VDW/vdwd.h new file mode 100755 index 0000000..c659c1d --- /dev/null +++ b/VDW/vdwd.h @@ -0,0 +1,117 @@ +/****************************************************************************** + * Product: # # ###### # # + * # # # # # # # ##### + * # # # # # # # # # + * # # # # # # # # # + * # # # # # # # # # + * # # # # # # # # # + * # ###### ## ## ##### + * + * File: vdwd.h + * Description: Header file for declaration of structures, datatypes etc for + * the Virtual Data Warehouse Daemon. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef VDWD_H +#define VDWD_H + +/* Return type definitions. +*/ +#define VDWD_OK 0 +#define VDWD_FAIL 1 +#define VDWD_EXIT 2 + +/* Definitions for maxims etc. +*/ +#define MAX_ERRMSG_LEN 256 +#define MAX_LOGFILELEN 256 + +/* Definitions for defaults. +*/ +#define DEF_SERVICENAME "vdwd" +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) +#define DEF_LOGFILE "/tmp/vdwd.log" +#endif +#if defined(_WIN32) +#define DEF_LOGFILE "\\VDWD.LOG" +#endif + +/* Define command line flags. +*/ +#define FLG_LOGFILE "-l" +#define FLG_LOGMODE "-m" + +/* Define error return codes which are embedded into returned error messages + * for the user to decipher. +*/ +#define VDWD_EMSG_BADSERVICE "V0001" +#define VDWD_EMSG_BADACK "V0002" +#define VDWD_EMSG_BADNAK "V0003" +#define VDWD_EMSG_BADDATA "V0004" + +/* Structure to provide the interface between the VDW Daemon and its + * linked in driver modules. Provides entry points into the driver for the + * daemon. +*/ +typedef struct { + int nType; + int (*InitService)( SERVICEDETAILS *, UCHAR * ); + int (*CloseService)( UCHAR * ); + int (*ProcessRequest)( UCHAR *, int, int (*)(UCHAR *, UINT), UCHAR * ); + void (*ProcessOOB)( UCHAR ); +} VDWD_DRIVERS; + +/* Globals (yuggghhh!) for the VDW Daemon. They are contained within a + * structure so they are more manageable and readers can see immediately + * the variables scope. +*/ +typedef struct { + int nActiveService; + UINT nLogMode; + UINT nServiceInitialised; + UCHAR szLogFile[MAX_LOGFILELEN]; +} VDWD_GLOBALS; + +/* Declare any globals required by the daemon, or any specifics to the + * C module. +*/ +#if defined(VDWD_C) + static VDWD_GLOBALS VDWD={0, LOG_DEBUG, FALSE, ""}; + extern VDWD_DRIVERS Driver[]; +#endif + +/* Prototypes for functions within VDWd +*/ +int GetConfig( int, UCHAR **, char **, UCHAR * ); +int VDWDInit( UCHAR * ); +int VDWDClose( UCHAR * ); +int VDWDInitService( int, SERVICEDETAILS *, UCHAR * ); +int VDWDCloseService( int, UCHAR * ); +int VDWDProcessRequest( int, UCHAR *, UINT, UCHAR * ); +int VDWDSendToClient( UCHAR *, UINT ); +int VDWDDataCallback( UCHAR *, int, UCHAR * ); +void VDWDOOBCallback( UCHAR ); +int main( int, char **, char ** ); + +#endif /* VDWD_H */ diff --git a/ux/.pure b/ux/.pure new file mode 100755 index 0000000..e69de29 diff --git a/ux/Makefile b/ux/Makefile new file mode 100755 index 0000000..39af4dd --- /dev/null +++ b/ux/Makefile @@ -0,0 +1,150 @@ +#****************************************************************************** +#* Product: # # # # # ### ###### +#* # # # # # # # # +#* # # # # # # # # +#* # # # # # ###### +#* # # # # # # # # +#* # # # # # # # # +#* ##### # # ####### ####### ### ###### +#* +#* File: Makefile +#* Description: Build description file for UX lib. +#* 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 . +#****************************************************************************** +TITLE = "UniX Library" +COPYRIGHT = "(C) P.D. Smart %D%, Version %I%" +PROJ = +PURIFY = #purify +PROJPATH = /users/bopropsm/ux +INCLUDEDIR = -I. +IM_INCLUDE = -I/usr/5include +1DEBUGFLAGS = -g # -DUX_DEBUG #-E +4DEBUGFLAGS = -g # -DUX_DEBUG #-E +5DEBUGFLAGS = -g # -DUX_DEBUG #-E +1OPTIMIZEFLAGS = #-O2 +4OPTIMIZEFLAGS = #-O2 +5OPTIMIZEFLAGS = #-O2 +1OPTIONFLAGS = -D${OS} #-DUX_MONITOR #-ansi -Wall +4OPTIONFLAGS = -D${OS} #-DUX_MONITOR #-ansi -Wall +5OPTIONFLAGS = -D${OS} -D_REENTRANT +CFLAGS = $(${OSVER}DEBUGFLAGS) $(${OSVER}OPTIMIZEFLAGS) \ + $(${OSVER}OPTIONFLAGS) +LDFLAGS = -static +SCCSFLAGS = -d$(PROJPATH) +SCCSGETFLAGS = + +ifeq ($(ZPU_BUILD),) +BASE = +else +BASE = zpu-elf- +endif + +CC = $(BASE)gcc +LD = $(BASE)gcc +AS = $(BASE)as +AR = $(BASE)ar +CP = $(BASE)objcopy +DUMP = $(BASE)objdump +RANLIB = $(BASE)ranlib + +# Suffixes where interested in for this project. +# +.SUFFIXES: +.SUFFIXES: .o .c .h + +# Our way of making an object file. +# +.c.o: + $(PURIFY) $(CC) $(INCLUDEDIR) $(CFLAGS) -c $< + +# All, ie: all programs to be built +# +all: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Use 'build' command to make LIBUX library." + +libux: Begin \ + libux.a \ + End + +# How to clean up the directory... make it look pretty! +# +clean: Begin \ + DoClean \ + End + +# How to perform an installation of the resultant software. +# +install: Begin \ + DoInstall \ + End +# +# Pre-make start sequence. +# +Begin: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Operation commencing @ `date`" + @echo + +# +# Post-make completion sequence. +# +End: + @echo + @echo "Completed @ `date`" + +# Perform all cleanup operations to ensure future builds occur with +# completeness. +# +DoClean: + rm -f *.o *.bak *.a *.BAK *.sav core + +# Perform installation of software as per spec. +# +DoInstall: + +# Build the UniX Library. +# +libux.a: ux_cli.o ux_cmprs.o ux_comms.o ux_lgr.o ux_linkl.o \ + ux_mon.o ux_str.o ux_thrd.o + $(AR) rcv libux.a \ + ux_cli.o ux_cmprs.o ux_comms.o ux_lgr.o \ + ux_linkl.o ux_mon.o ux_str.o ux_thrd.o + +ux_cli.o: ux_cli.c ux_comon.h ux_dtype.h ux_comms.h + +ux_cmprs.o: ux_cmprs.c + +ux_comms.o: ux_comms.c ux_comms.h ux_dtype.h ux_comon.h + +ux_lgr.o: ux_lgr.c ux_comon.h ux_dtype.h ux_comms.h + +ux_linkl.o: ux_linkl.c ux_comon.h ux_dtype.h ux_comms.h + +ux_mon.o: ux_mon.c ux_mon.h ux_comon.h ux_dtype.h ux_comms.h + +ux_str.o: ux_str.c ux_comon.h ux_dtype.h ux_comms.h + +ux_thrd.o: ux_thrd.c ux_comon.h ux_dtype.h ux_comms.h diff --git a/ux/build b/ux/build new file mode 100755 index 0000000..2412633 --- /dev/null +++ b/ux/build @@ -0,0 +1,184 @@ +#!/bin/csh +#****************************************************************************** +#* Product: # # # # # ### ###### +#* # # # # # # # # +#* # # # # # # # # +#* # # # # # ###### +#* # # # # # # # # +#* # # # # # # # # +#* ##### # # ####### ####### ### ###### +#* +#* File: build +#* Description: Build description file for making different versions of the +#* UX library. +#* 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 . +#****************************************************************************** + +# Work out architecture we are compiling on.. +# +setenv ARCH `uname -s` +setenv ARCH ${ARCH}`uname -r | cut -c1` + +# Work out arguments and decide on actions from there. +# +if( $#argv > 0 ) then + + switch("$argv[1]") + + case "save": + make -f Makefile clean + sccs delta SCCS + exit 0 + + case "install": + echo "Installing libraries and header files." + echo -n "Please enter 'vdwd' " + su - vdwd <. + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef UX_H +#define UX_H + +#include "ux_dtype.h" +#include "ux_comon.h" +#include "ux_comms.h" +#include "ux_cmprs.h" +#include "ux_mon.h" + +/* Version Control. +*/ +#define PROGRAM_VERSION "%I%" + +#endif /* UX_H */ diff --git a/ux/ux_cli.c b/ux/ux_cli.c new file mode 100755 index 0000000..f2a6da7 --- /dev/null +++ b/ux/ux_cli.c @@ -0,0 +1,174 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_cli.c + * Description: Unix or Windows Command Line Processing functions. + * + * Version: %I% + * Dated: %D% + * Author: P.D. Smart + * 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 . + ****************************************************************************** + +/* Bring in system header files. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(SUNOS) || defined(SOLARIS) || defined(LINUX) +#include +#include +#include +#endif + +#if defined(SOLARIS) +#include +#endif + +#if defined(LINUX) +#include +#endif + +#if defined(_WIN32) +#include +#include +#endif + +#if defined(SUNOS) || defined(SOLARIS) +#include +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define UX_CLI_C + +/* Bring in specific header files. +*/ +#include "ux.h" + +/****************************************************************************** + * Function: GetCLIParam + * Description: Get a Command Line Interface Parameter from the command line + * of the shell which started this program... Confusing! + * Returns: R_OK - Parameter obtained. + * R_FAIL - Parameter does not exist. + ******************************************************************************/ +int GetCLIParam( int nArgc, /* I: Argc or equiv */ + UCHAR **Argv, /* IO: Argv or equiv */ + UCHAR *szParm, /* I: Param flag to look for */ + UINT nParmType, /* I: Type of param (ie int) */ + UCHAR *pVar, /* O: Pointer to variable for parm */ + UINT nVarLen, /* I: Length pointed to by pVar */ + UINT nZapParam ) /* I: Delete argument after proc */ + +{ + /* Local variables. + */ + int nNdx; + int nReturn = R_FAIL; + UCHAR *pArg = NULL; + + /* Scan through the command line, seeing if the required flag exists. + */ + for(nNdx=1; nNdx < nArgc; nNdx++) + { + if(strcasecmp(Argv[nNdx], szParm) == 0) + { + /* Has the user provided a parameter? + */ + if( strlen(Argv[nNdx]) < strlen(szParm) ) + { + pArg = Argv[nNdx]+strlen(Argv[nNdx]); + } else + if( nNdx+1 <= nArgc ) + { + pArg = Argv[nNdx+1]; + } else pArg = NULL; + break; + } + } + + /* Found the required flag? + */ + if( nNdx != nArgc && pArg != NULL ) + { + /* Convert the parameter into the required destination format. + */ + switch( nParmType ) + { + case T_INT: + if(sscanf(pArg, "%d", (UINT *)pVar) == 1) + nReturn = R_OK; + break; + + case T_STR: + if(sscanf(pArg, "%s", (UCHAR *)pVar) == 1) + nReturn = R_OK; + break; + + case T_CHAR: + if(sscanf(pArg, "%c", (UCHAR *)pVar) == 1) + nReturn = R_OK; + break; + + case T_LONG: + if(sscanf(pArg, "%ld", (ULNG *)pVar) == 1) + nReturn = R_OK; + break; + + default: + break; + } + + /* If the caller has requested that the command line argument + * be deleted for security purposes, delete! + */ + if(nZapParam == TRUE) + { + memset(pArg, '*', strlen(pArg)); + } + } else + /* If a valid flag appeared, but it did not have an argument, then + * assume its a state flag, so set the var parameter to NULL but return + * success. + */ + if( nNdx != nArgc && pArg == NULL ) + { + pVar = NULL; + nReturn = R_OK; + } + + /* Finished, get out!! + */ + return( nReturn ); +} diff --git a/ux/ux_cmprs.c b/ux/ux_cmprs.c new file mode 100755 index 0000000..7c42b3f --- /dev/null +++ b/ux/ux_cmprs.c @@ -0,0 +1,692 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_cmprs.c + * Description: Routines to compress/decompress data. The basic code stems + * from a LINUX public domain lzw compression/decompression + * algorithm, basically tidied up a little as it looked awful and + * enhanced to allow embedding within programs. Eventually, a more + * hi-tech algorithm will be implemented, but for now, this + * lzw appears to have very high compression ratio's + * on text. + * + * 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 . + ******************************************************************************/ + +/* Bring in system header files. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(SUNOS) || defined(SOLARIS) || defined(LINUX) +#include +#include +#include +#endif + +#if defined(SOLARIS) +#include +#endif + +#if defined(LINUX) +#include +#endif + +#if defined(_WIN32) +#include +#include +#endif + +#if defined(SUNOS) || defined(SOLARIS) +#include +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define UX_CMPRS_C + +/* Bring in specific header files. +*/ +#include "ux.h" + +#define LZSIZE 4096 /* Should stay < 32768 */ +#define CLEAR 256 /* Clear Code */ +#define REPEAT 257 +#define START 258 + +static code Prefix, Prefix0, Index; +static int code_len, new_entry; /* For repeated strings */ +static int bits, off, size; +static code *scode; +static byte *sbyte; +static unsigned int pcode, pbyte, length; +static int nInit = 0; +static int nNdx; +static unsigned short nEndian = 0xff00; +static unsigned char *pEndian = (unsigned char *)&nEndian; +static unsigned char *pC, cTmp; + +code *PTable, *NTable; +byte *CTable; + +/****************************************************************************** + * Function: Compress + * Description: A generic function to compress a buffer of text. + * Returns: NULL - Memory problems. + * Memory buffer containing compressed copy of input. + ******************************************************************************/ +UCHAR *Compress( UCHAR *spInBuf, /* I: Buffer to be compressed. */ + UINT *nLen ) /* IO: Length of dec/compressed buffer. */ +{ + /* Local variables. + */ + int nSize; + UINT nInLen = *nLen; + UINT nOutLen = nInLen+10; + UCHAR *spReturn = spInBuf; + UCHAR *spOut; + char *szFunc = "Compress"; + + /* If the input buffer is smaller than a given threshold then dont + * waste CPU trying to compress it. + */ + if(nInLen < MIN_COMPRESSLEN) + { + return(spInBuf); + } + + /* Allocate a buffer to hold the compressed data. + */ + if((spOut=(UCHAR *)malloc(sizeof(USHRT) * nOutLen)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", nOutLen); + Errno = E_NOMEM; + return(NULL); + } + + /* Compress the buffer. + */ + nSize=WLZW(spInBuf, (unsigned short *)&spOut[6], nInLen, nOutLen-4); + + /* Could we compress it? + */ + if(nSize > 0 && (UINT)nSize < nInLen) + { + /* OK, add in the compressed id-byte and the original buffer size. + */ + spOut[0] = spOut[1] =0xff; + PutCharFromLong( &spOut[2], (ULNG)nInLen ); + spReturn = spOut; + if(nLen != NULL) *nLen = nSize + 6; + +/* Debugging code. +*/ +#if defined(UX_DEBUG) + printf("Compressed from (%d) to (%d) bytes\n", nInLen, nSize); +#endif + } else + { + Lgr(LOG_DEBUG, szFunc, "Couldnt compress data (%d)", nSize); + if(nLen != NULL) *nLen = nInLen; + free(spOut); + } + + /* Return buffer or NULL to caller. + */ + return(spReturn); +} + +/****************************************************************************** + * Function: Decompress + * Description: A generic function to de-compress a buffer to text. + * Returns: NULL - Memory problems. + * Memory buffer containing decompressed copy of input. + ******************************************************************************/ +UCHAR *Decompress( UCHAR *spInBuf, /* I: Buffer to be decompressed. */ + UINT *nCmpLen ) /* IO: Length of comp/dec buffer */ +{ + /* Local variables. + */ + UINT nSize; + UINT nOutLen; + UINT nShift; + UCHAR *spTmp; + UCHAR *spReturn = spInBuf; + char *szFunc = "Decompress"; + + /* Is the input buffer in compressed format..? The first byte should + * contain the value 0xff if its compressed. + */ + if(spInBuf[0] == 0xff && spInBuf[1] == 0xff) + { + /* Extract the expanded size from the buffer. + */ + nOutLen = GetLongFromChar(&spInBuf[2]); + + /* Allocate a buffer to hold the de-compressed data. + */ + if((spTmp=(UCHAR *)malloc(nOutLen+2)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", nOutLen); + Errno = E_NOMEM; + return(NULL); + } + + /* Ensure that the data beings on a LONG boundary as the compression/ + * Decompression works with integers/shorts. + */ + nShift=(int)((long)(&spInBuf[6]) % sizeof(long)); + + /* Shift the memory block onto a long boundary. + */ + memcpy(&spInBuf[6-nShift], &spInBuf[6], (*nCmpLen)-(6-nShift)); + + /* Decompress data. + */ + if((nSize=RLZW((unsigned short *)&spInBuf[6-nShift], spTmp, + (*nCmpLen)-6, nOutLen)) <= 0) + { + Lgr(LOG_WARNING,szFunc, "Couldnt Decompress data (%s)",nSize); + free(spTmp); + return(NULL); + } + + /* Terminate string as this may just be a character string. + */ + spTmp[nOutLen] = '\0'; + + /* Setup pointer to new buffer containing decompressed data. + */ + spReturn = spTmp; + + /* Update the callers length parameter to indicate buffers new + * length. + */ + *nCmpLen = nOutLen; + } else + { + /* The buffer is not compressed, terminate it in case its a character + * string. No need to update the callers lenght parameter as it + * has not changed. + */ + spInBuf[*nCmpLen] = '\0'; + +/* Debugging code. +*/ +#if defined(UX_DEBUG) + printf("Buffer not compressed (%c, %x)\n", spInBuf[0], spInBuf[1]); +#endif + } + + /* Return buffer or NULL to caller. + */ + return(spReturn); +} + +/* + * LZW_init - Initialise compression routines. +*/ +void LZW_init() +{ + PTable = (code *) malloc(LZSIZE*sizeof(code)); + NTable = (code *) malloc(LZSIZE*sizeof(code)); + CTable = (byte *) malloc(LZSIZE*sizeof(byte)); +} + +/* Write Next Code */ +static int WCode(code wcode) +{ + /* Local variables. + */ + int todo; + + pcode+=bits; + + /* Quickly check output buffer size for insufficient compression. + */ + if((pcode>>3) > length) + return 0; + + if((todo = bits+off-16)>=0) + { + *scode++ |= wcode>>todo; + *scode = wcode<<(16-todo); + off = todo; + } else + { + *scode |= wcode<<(-todo); + off += bits; + } + + /* Finished, get out. + */ + return(1); +} + +/* Read Next Code */ +static short RCode(void) +{ + /* Local variables. + */ + code rcode; /* 15 bits maximum, with LZSIZE=32768; never negative */ + short todo; + + if((todo = bits+off-16)>=0) + { + rcode = (*scode++)<>(16-todo); + off = todo; + } else + { + rcode = (*scode)>>(-todo); + off += bits; + } + + rcode&=size-1; + return((short)rcode); +} + +/* Initialization (R/W) */ +static void InitTable(void) +{ + bits = 8; + size = 256; + pbyte = pcode = off = 0; + + memset(PTable, -1, LZSIZE*sizeof(code)); + Index = START; +} + +/* Initialization for decompression */ +static void RInitTable(void) +{ + /* Local variables. + */ + int i; + byte *p; + + InitTable(); + for(i = 0, p = CTable; i < CLEAR; i++) + *p++ = i; +} + +/* Lookup Table */ +static short LookUp(byte Car) +{ + /* Local variables. + */ + code pi; + + pi = PTable[Prefix]; + while(((short)pi) != -1) + { + if(CTable[pi] == Car) return pi; + pi = NTable[pi]; + } + + /* No hit. + */ + return(-1); +} + +/* byte is added to table and becomes prefix */ +static int WAddPrefix(void) +{ + /* Local variables. + */ + code pi; + + pi = PTable[Prefix]; + PTable[Prefix] = Index; /* Next entry */ + NTable[Index] = pi; + CTable[Index++] = Prefix = Prefix0 = *sbyte; + + /* Table full. + */ + if(Index == LZSIZE) + { + if(!WCode(CLEAR)) return 0; + Index = START; + memset(PTable, -1, LZSIZE*sizeof(code)); + bits = 8; + size = 256; + } else + if(Index > size) + { + bits++; + size <<= 1; + } + + /* Finished, get out. + */ + return(1); +} + +static int Expand(code val) +{ + /* Local variables. + */ + code p,q,r; + + if(val > Index) + return -2; + q = -1; + PTable[Index] = Prefix; + do { + p = val; + + /* Get previous + */ + while((p = PTable[(r = p)]) != q); + + *sbyte++ = CTable[r]; + + /* Done; skip useless stuff + */ + if(++pbyte >= length) + return 0; + + if(((short)q) == -1 && new_entry) + { + if(Index == LZSIZE) return -3; + CTable[Index++] = CTable[r]; + new_entry = 0; + } + q = r; + } while(q != val); + Prefix = val; + + /* Finished, get out. + */ + return(1); +} + +int check_repeat(int len) +{ + /* Local variables. + */ + int n = 0; + char *s; + char *s0; + + /* Current string. + */ + s = sbyte; + + /* Reference string. + */ + s0 = s - code_len; + + while(code_len <= len && (n+1) < size) + { + if(memcmp(s0, s, code_len)) + break; + + /* Number of bytes left. + */ + len -= code_len; + + /* Number of repeats. + */ + n++; + s += code_len; + } + + /* Finished, get out! + */ + return(n); +} + +/****************************************************************************** + * Function: WLZW + * Description: Write or compress data in LZW format. + * Returns: 0 = Worthless CPU waste (No compression) + * -1 = General error + * -2 = Logical error + * -3 = Expand error + * >0 = OK/total length + ******************************************************************************/ +int WLZW( byte *si, /* I: Data for compression */ + code *so, /* O: Compressed data */ + int len, /* I: Length of data for compression */ + int maxlen ) /* I: Maximum length of compressed data */ +{ + /* Local variables. + */ + code val; + int Repeat; + + if(nInit == 0) + { + LZW_init(); + nInit = 1; + } + + scode = so; + *scode = 0; + sbyte = si; + length = maxlen; + InitTable(); + + Prefix = Prefix0 = *sbyte; + code_len = 0; + while(++pbyte 1) + { + /* Check for string repeat. If positive, we are going to write + * 3 codes. Therefore, we need to filter small repeats. + */ + Repeat = check_repeat(len-pbyte); + + /* Could do better. + */ + if(Repeat > 1) + { + /* At least 3 times (1+>2). + */ + WCode(REPEAT); + WCode(Repeat); + if(!WCode(Prefix)) + return(0); + + /* Total length of repeat. + */ + pbyte += code_len*Repeat; + if(pbyte >= len) + break; + + /* Position of next byte. + */ + sbyte += code_len*Repeat; + } + } + + /* Break in sequence. + */ + if(((short)(val=LookUp(*sbyte)))==-1) + { + /* Write to buffer. + */ + if(!WCode(Prefix)) + return(0); + + /* Car is new prefix. + */ + if(!WAddPrefix()) + return(0); + code_len = 0; + } else + Prefix = val; + } + + if(!WCode(Prefix)) + return(0); + + len = pcode/sizeof(code)/8; + if(pcode&(sizeof(code)*8-1)) + len++; + len *= sizeof(code); + + /* Portability issue, will change the code to be insensitive to + * little/big endian eventually. + */ + if(*pEndian == 0x00) + { + pC = (unsigned char *)so; + for(nNdx=0; nNdx < len; nNdx += 2, pC += 2) + { + cTmp = *pC; + *pC = *(pC+1); + *(pC+1) = cTmp; + } + } + return(len); +} + +/* CLEAR Code Read */ +static int RClear(void) +{ + bits = 8; + size = 256; + Prefix = RCode(); + *sbyte++ = Prefix; + if(++pbyte >= length) + return(0); + new_entry = 1; + Index = START; + bits++; + size = 512; + + /* Finished, get out! + */ + return(1); +} + +/****************************************************************************** + * Function: RLZW + * Description: Read or de-compress data from LZW format. + * Returns: 0 = Worthless CPU waste (No compression) + * -1 = General error + * -2 = Logical error + * -3 = Expand error + * >0 = OK/total length + ******************************************************************************/ +int RLZW( code *si, /* I: Data to be decompressed */ + byte *so, /* O: Decompressed data */ + int silen, /* I: Compressed length */ + int len ) /* I: Expected length of decompressed data. */ +{ + /* Local variables. + */ + code val; + int n; + + /* Portability issue, will change the code to be insensitive to + * little/big endian eventually. + */ + if(*pEndian == 0x00) + { + pC = (unsigned char *)si; + for(nNdx=0; nNdx < silen; nNdx += 2, pC += 2) + { + cTmp = *pC; + *pC = *(pC+1); + *(pC+1) = cTmp; + } + } + + if(nInit == 0) + { + LZW_init(); + nInit = 1; + } + + sbyte = so; + scode = si; + length = len; + RInitTable(); + + RClear(); + for(;;) + { + val = RCode(); + if(val == CLEAR) + { + if(!RClear()) + break; + continue; + } + + if(val == REPEAT) + { + /* # of repeats. + */ + len = RCode(); + val = RCode(); /* code to expand */ + do { + n = Expand(val); + } while(--len && n > 0); + } else + { + n = Expand(val); + if(Index >= size) + { + bits++; + size <<= 1; + } + + /* Add new entry at next code. + */ + new_entry = 1; + } + if(n < 0) + return(n); + if(n == 0) + break; + } + + /* Finished, get out! + */ + return(1); +} diff --git a/ux/ux_cmprs.h b/ux/ux_cmprs.h new file mode 100755 index 0000000..a6b8e0a --- /dev/null +++ b/ux/ux_cmprs.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_cmprs.h + * Description: General purpose compression routines. + * + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef UX_CMPRS_H +#define UX_CMPRS_H + +/* Typedefs used within the module. +*/ +typedef unsigned short code; +typedef unsigned char byte; + +/* Define prototypes for functions globally available. +*/ +int WLZW( byte *, code *, int, int ); +int RLZW( code *, byte *, int, int ); + +#endif /* UX_CMPRS_H */ diff --git a/ux/ux_comms.c b/ux/ux_comms.c new file mode 100755 index 0000000..1a72151 --- /dev/null +++ b/ux/ux_comms.c @@ -0,0 +1,2399 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_comms.c + * Description: Generic communications routines. + * + * 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 . + ******************************************************************************/ + +/* Bring in system header files. +*/ +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#include +#include +#include +#endif + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(SOLARIS) +#include +#endif + +#include +#include + +/* Indicate that we are a C module for any header specifics. +*/ +#define UX_COMMS_C + +/* Bring in specific header files. +*/ +#include "ux.h" + +/* Local module variables. +*/ +static SL_GLOBALS Sl; + +/* 16 bit CRC lookup table. +*/ +SL_CRCTAB tCRCTable[] = { + { 0, 0 }, { 193, 192 }, { 129, 193 }, { 64, 1 }, + { 1, 195 }, { 192, 3 }, { 128, 2 }, { 65, 194 }, + { 1, 198 }, { 192, 6 }, { 128, 7 }, { 65, 199 }, + { 0, 5 }, { 193, 197 }, { 129, 196 }, { 64, 4 }, + { 1, 204 }, { 192, 12 }, { 128, 13 }, { 65, 205 }, + { 0, 15 }, { 193, 207 }, { 129, 206 }, { 64, 14 }, + { 0, 10 }, { 193, 202 }, { 129, 203 }, { 64, 11 }, + { 1, 201 }, { 192, 9 }, { 128, 8 }, { 65, 200 }, + { 1, 216 }, { 192, 24 }, { 128, 25 }, { 65, 217 }, + { 0, 27 }, { 193, 219 }, { 129, 218 }, { 64, 26 }, + { 0, 30 }, { 193, 222 }, { 129, 223 }, { 64, 31 }, + { 1, 221 }, { 192, 29 }, { 128, 28 }, { 65, 220 }, + { 0, 20 }, { 193, 212 }, { 129, 213 }, { 64, 21 }, + { 1, 215 }, { 192, 23 }, { 128, 22 }, { 65, 214 }, + { 1, 210 }, { 192, 18 }, { 128, 19 }, { 65, 211 }, + { 0, 17 }, { 193, 209 }, { 129, 208 }, { 64, 16 }, + { 1, 240 }, { 192, 48 }, { 128, 49 }, { 65, 241 }, + { 0, 51 }, { 193, 243 }, { 129, 242 }, { 64, 50 }, + { 0, 54 }, { 193, 246 }, { 129, 247 }, { 64, 55 }, + { 1, 245 }, { 192, 53 }, { 128, 52 }, { 65, 244 }, + { 0, 60 }, { 193, 252 }, { 129, 253 }, { 64, 61 }, + { 1, 255 }, { 192, 63 }, { 128, 62 }, { 65, 254 }, + { 1, 250 }, { 192, 58 }, { 128, 59 }, { 65, 251 }, + { 0, 57 }, { 193, 249 }, { 129, 248 }, { 64, 56 }, + { 0, 40 }, { 193, 232 }, { 129, 233 }, { 64, 41 }, + { 1, 235 }, { 192, 43 }, { 128, 42 }, { 65, 234 }, + { 1, 238 }, { 192, 46 }, { 128, 47 }, { 65, 239 }, + { 0, 45 }, { 193, 237 }, { 129, 236 }, { 64, 44 }, + { 1, 228 }, { 192, 36 }, { 128, 37 }, { 65, 229 }, + { 0, 39 }, { 193, 231 }, { 129, 230 }, { 64, 38 }, + { 0, 34 }, { 193, 226 }, { 129, 227 }, { 64, 35 }, + { 1, 225 }, { 192, 33 }, { 128, 32 }, { 65, 224 }, + { 1, 160 }, { 192, 96 }, { 128, 97 }, { 65, 161 }, + { 0, 99 }, { 193, 163 }, { 129, 162 }, { 64, 98 }, + { 0, 102 }, { 193, 166 }, { 129, 167 }, { 64, 103 }, + { 1, 165 }, { 192, 101 }, { 128, 100 }, { 65, 164 }, + { 0, 108 }, { 193, 172 }, { 129, 173 }, { 64, 109 }, + { 1, 175 }, { 192, 111 }, { 128, 110 }, { 65, 174 }, + { 1, 170 }, { 192, 106 }, { 128, 107 }, { 65, 171 }, + { 0, 105 }, { 193, 169 }, { 129, 168 }, { 64, 104 }, + { 0, 120 }, { 193, 184 }, { 129, 185 }, { 64, 121 }, + { 1, 187 }, { 192, 123 }, { 128, 122 }, { 65, 186 }, + { 1, 190 }, { 192, 126 }, { 128, 127 }, { 65, 191 }, + { 0, 125 }, { 193, 189 }, { 129, 188 }, { 64, 124 }, + { 1, 180 }, { 192, 116 }, { 128, 117 }, { 65, 181 }, + { 0, 119 }, { 193, 183 }, { 129, 182 }, { 64, 118 }, + { 0, 114 }, { 193, 178 }, { 129, 179 }, { 64, 115 }, + { 1, 177 }, { 192, 113 }, { 128, 112 }, { 65, 176 }, + { 0, 80 }, { 193, 144 }, { 129, 145 }, { 64, 81 }, + { 1, 147 }, { 192, 83 }, { 128, 82 }, { 65, 146 }, + { 1, 150 }, { 192, 86 }, { 128, 87 }, { 65, 151 }, + { 0, 85 }, { 193, 149 }, { 129, 148 }, { 64, 84 }, + { 1, 156 }, { 192, 92 }, { 128, 93 }, { 65, 157 }, + { 0, 95 }, { 193, 159 }, { 129, 158 }, { 64, 94 }, + { 0, 90 }, { 193, 154 }, { 129, 155 }, { 64, 91 }, + { 1, 153 }, { 192, 89 }, { 128, 88 }, { 65, 152 }, + { 1, 136 }, { 192, 72 }, { 128, 73 }, { 65, 137 }, + { 0, 75 }, { 193, 139 }, { 129, 138 }, { 64, 74 }, + { 0, 78 }, { 193, 142 }, { 129, 143 }, { 64, 79 }, + { 1, 141 }, { 192, 77 }, { 128, 76 }, { 65, 140 }, + { 0, 68 }, { 193, 132 }, { 129, 133 }, { 64, 69 }, + { 1, 135 }, { 192, 71 }, { 128, 70 }, { 65, 134 }, + { 1, 130 }, { 192, 66 }, { 128, 67 }, { 65, 131 }, + { 0, 65 }, { 193, 129 }, { 129, 128 }, { 64, 64 }}; + +/****************************************************************************** + * Function: _SL_CalcCRC + * Description: Calculate the CRC on a buffer. + * Thread Safe: Yes + * Returns: 16bit CRC + ******************************************************************************/ +UINT _SL_CalcCRC( UCHAR *szBuf, /* I: Data buffer to perform CRC on */ + UINT nBufLen ) /* I: Length of data buffer */ +{ + /* Local variables. + */ + UINT nCRC = 0; + UINT nNdx; + UINT nTabNdx; + UCHAR cHiCRC = 0; + UCHAR cLoCRC = 0; + + /* Loop through each byte, using the CRC table to update the CRC value. + */ + for(nNdx=0; nNdx < nBufLen; nNdx++) + { + nTabNdx = cHiCRC ^ (UCHAR)szBuf[nNdx]; + cHiCRC = cLoCRC ^ tCRCTable[nTabNdx].cHiCRC; + cLoCRC = tCRCTable[nTabNdx].cLoCRC; + } + + /* Place CRC lower and upper bytes into one integer for caller. + */ + nCRC = cLoCRC | (cHiCRC << 8); + + /* Return calculated CRC to caller. + */ + return(nCRC); +} + +/****************************************************************************** + * Function: _SL_CheckCRC + * Description: Validate the CRC on a buffer with the one given. + * Thread Safe: Yes + * Returns: R_OK - CRC match. + * R_FAIL - CRC failure. + * + ******************************************************************************/ +UINT _SL_CheckCRC( UCHAR *szBuf, /* I: Data buffer to calc CRC on */ + UINT nBufLen ) /* I: Length of data in buffer */ +{ + /* Local variables. + */ + UINT nBufferCRC; + UINT nPacketCRC; + + /* Get packet CRC from buffer. + */ + nPacketCRC = szBuf[nBufLen] + (szBuf[nBufLen-1] << 8); + + /* Calculate comparison CRC based on data in buffer. + */ + nBufferCRC = _SL_CalcCRC( szBuf, nBufLen-2) ; + +/* Debugging code. +*/ +#if defined(UX_DEBUG) + if(nPacketCRC != nBufferCRC) + { + printf("CRC failed (%u, %u)\n", nPacketCRC, nBufferCRC); + fflush(stdout); + } +#endif + + /* Passed or failed? + */ + return(nPacketCRC == nBufferCRC ? R_OK : R_FAIL); +} + +/****************************************************************************** + * Function: _SL_FdBlocking + * Description: Set a file descriptors blocking mode. + * Thread Safe: Yes + * Returns: R_OK - Mode set. + * R_FAIL - Couldnt set mode. + ******************************************************************************/ +int _SL_FdBlocking( int nFd, /* File descr to perform action on*/ + int nBlock ) /* Block (1) or non-blocking (0) */ +{ + /* Local variables. + */ + int nReturn = R_FAIL; +#if defined(_WIN32) + ULNG lBlock; +#endif + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) + /* Set the mode accordingly. + */ + if(fcntl(nFd, F_SETFL, nBlock == 1 ? 0 : FNDELAY) >= 0) + nReturn = R_OK; +#endif + +#if defined(_WIN32) + /* Set up blocking flag. + */ + lBlock = (nBlock == 1 ? 0L : 1L); + + /* Set the mode accordingly. + */ + if(ioctlsocket(nFd, FIONBIO, &lBlock) == 0) + nReturn = R_OK; +#endif + + /* Finished, get out!! + */ + return( nReturn ); +} + +/****************************************************************************** + * Function: _SL_AcceptClient + * Description: Accept an incoming request from a client. Builds a duplicate + * table entry for the client (if one doesnt already exist) and + * allocates a unique Channel Id to it. + * Thread Safe: No, ensures only SL library thread may enter. + * Returns: R_OK - Comms functionality initialised. + * R_FAIL - Initialisation failed, see Errno. + * E_NOMEM - Memory exhaustion. + ******************************************************************************/ +int _SL_AcceptClient( UINT nServerSd, /* I: Server Socket Id */ + SL_NETCONS *spServer, /* I: Server descr record */ + SL_NETCONS **spNewClnt ) /* O: New client */ +{ + /* Local variables. + */ + struct linger sLinger; + struct sockaddr_in sPeer; + int nReturn = R_FAIL; + UINT nChanId = DEF_CHANID; + UINT nResult = sizeof(sPeer); + int nTmpSd; + char *szFunc = "_SL_AcceptClient"; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + + SL_THREAD_ONLY; + + /* Accept the connection to yield client information. + */ + if( (nTmpSd=accept(nServerSd, (struct sockaddr *)&sPeer, &nResult)) < 0 ) + { + Errno = E_BADACCEPT; + return(nReturn); + } + + /* Need to find the next channel ID. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); + spNetCon != NULL; spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + /* Find the greatest Channel Id. + */ + if(spNetCon->nChanId > nChanId) + nChanId = spNetCon->nChanId; + } + + /* New entry, need to duplicate masters record. + */ + if((spNetCon=(SL_NETCONS *)malloc(sizeof(SL_NETCONS))) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + sizeof(SL_NETCONS)); + Errno = E_NOMEM; + } else + { + /* Duplicate!!! Watch for dynamic copyrights! + */ + memcpy((UCHAR *)spNetCon, (UCHAR *)spServer, sizeof(SL_NETCONS)); + + /* Try and allocate an initial receive buffer. + */ + if((spNetCon->spRecvBuf=(UCHAR *)malloc(DEF_INITRECVBUF))==NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + DEF_INITRECVBUF); + Errno = E_NOMEM; + free(spNetCon); + } else + { + /* Add in new specific information. + */ + spNetCon->nSd = nTmpSd; + spNetCon->nServerPortNo = ntohs(sPeer.sin_port); + spNetCon->lServerIPaddr = ntohl(sPeer.sin_addr.s_addr); + spNetCon->nChanId = nChanId + 1; + spNetCon->nStatus = SSL_UP; + spNetCon->spXmitBuf = NULL; + spNetCon->nXmitLen = 0; + spNetCon->nXmitPos = 0; + + /* Set up KEEPALIVE, so the underlying keeps an eye on the net/ + * processes going up/down. + */ + if( setsockopt(spNetCon->nSd, SOL_SOCKET, SO_KEEPALIVE, + (UCHAR *)&Sl.nSockKeepAlive, + sizeof(Sl.nSockKeepAlive)) < 0 ) + { + Lgr(LOG_WARNING, szFunc, + "Couldnt set KEEPALIVE on socket (%d)", spNetCon->nSd); + } + + /* Set up LINGER to be disabled, if we die unexpectedly, the socket + * /ports should be freed up. We lose data, but nothing can be done. + */ + sLinger.l_onoff = 0; + sLinger.l_linger = 0; + if( setsockopt(spNetCon->nSd, SOL_SOCKET, SO_LINGER, + (UCHAR *)&sLinger, sizeof(struct linger)) < 0 ) + { + Lgr(LOG_WARNING, szFunc, + "Couldnt disable LINGER on socket (%d)", spNetCon->nSd); + } + + /* Set the socket to non-blocking mode, not prepared for anything + * to bring us to a halt! + */ + _SL_FdBlocking(spNetCon->nSd, 0); + + /* Place in NetCon list. + */ + if(AddItem(&Sl.spHead, &Sl.spTail, SORT_NONE, + &spNetCon->nChanId, + &spNetCon->lServerIPaddr, + spNetCon->szServerName, + spNetCon) == R_OK) + { + /* Finally, call the users control callback to let him + * know about the new connection. + */ + spNetCon->nCntrlCallback(SLC_NEWSERVICE, spNetCon->nChanId, + _SL_GetPortNo(spNetCon), + spNetCon->lServerIPaddr, + spNetCon->nOurPortNo); + nReturn = R_OK; + } else + { + free(spNetCon->spRecvBuf); + free(spNetCon); + } + + /* If the caller requires a pointer to the new clients control + * record then set it up for passback. + */ + if(spNewClnt != NULL) + { + *spNewClnt = spNetCon; + } + } + } + + /* Finished, get out!! + */ + return( nReturn ); +} + +/****************************************************************************** + * Function: _SL_GetPortNo + * Description: Get valid port number from structures. + * Thread Safe: Yes + * Returns: Port Number. + ******************************************************************************/ +UINT _SL_GetPortNo( SL_NETCONS *spNetCon ) /* I: Connection description */ +{ + /* Local variables. + */ + UINT nReturn; + + /* Work out correct callback port number. + */ + nReturn = (spNetCon->nServerPortNo == 0 ? spNetCon->nOurPortNo : + spNetCon->nServerPortNo); + + /* Return valid port number. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SL_Close + * Description: Close a client or server connection. + * Thread Safe: No, forces SL thread entry only. + * Returns: R_OK - Connection closed successfully. + * R_FAIL - Failed to close connection, see Errno. + * + ******************************************************************************/ +int _SL_Close( SL_NETCONS *spNetCon, /* I: Connection to sever */ + UINT nDoCallback ) /* I: Perform closure callback? */ +{ + /* Local variables. + */ + int nReturn = R_OK; + UCHAR *szFunc = "_SL_Close"; + + SL_THREAD_ONLY; + + /* Close the port, no longer needed. + */ + SocketClose(spNetCon->nSd); + + /* OK, send a close/fail callback to user code if required. + */ + if(nDoCallback == TRUE) + { + spNetCon->nCntrlCallback(SLC_LINKFAIL, spNetCon->nChanId, + _SL_GetPortNo(spNetCon), spNetCon->lServerIPaddr, + spNetCon->nOurPortNo); + } + + /* Free up receive buffer, not needed. + */ + if(spNetCon->spRecvBuf != NULL) + { + free(spNetCon->spRecvBuf); + spNetCon->spRecvBuf = NULL; + } + + /* Free up transmit buffer, not needed. + */ + if(spNetCon->spXmitBuf != NULL) + { + free(spNetCon->spXmitBuf); + spNetCon->spXmitBuf = NULL; + } + + /* Free up control record, no longer needed. + */ + if(DelItem(&Sl.spHead,&Sl.spTail,spNetCon,NULL,NULL,NULL) + == R_FAIL) + { + Lgr(LOG_WARNING, szFunc, + "Couldnt delete server entry (%d, %d, %s)", + spNetCon->nChanId, spNetCon->nOurPortNo, + SL_HostIPtoString(spNetCon->lServerIPaddr)); + + /* Set return value to indicate failure. + */ + nReturn=R_FAIL; + } else + { + /* Free up the control memory. + */ + free(spNetCon); + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SL_ConnectToServer + * Description: Attempt to make a connection with a remote server. + * Thread Safe: No, forces SL thread entry only. + * Returns: R_OK - + * R_FAIL - + * E_NOMEM - Memory exhaustion. + * E_NOSOCKET - Couldnt allocate a socket for connection. + ******************************************************************************/ +int _SL_ConnectToServer( SL_NETCONS *spNetCon ) +{ + /* Local variables. + */ +#if defined(_WIN32) + int nWinErr; +#endif + char *szFunc = "_SL_ConnectToServer"; + struct linger sLinger; + struct sockaddr_in sServer; + + SL_THREAD_ONLY; + + /* Build up info of the Server we intend to connect to. + */ + memset((UCHAR *)&sServer, '\0', sizeof(struct sockaddr)); + sServer.sin_family = AF_INET; + sServer.sin_addr.s_addr = htonl(spNetCon->lServerIPaddr); + sServer.sin_port = htons((USHRT)spNetCon->nServerPortNo); + + /* If required (nearly always), create an end point (socket) for our + * side of the communications link. + */ + if(spNetCon->nSd == -1) + { + if((spNetCon->nSd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + Errno = E_NOSOCKET; + return(R_FAIL); + } else + { + /* Set the socket to non-blocking mode, not prepared for anything + * to bring us to a halt! + */ + _SL_FdBlocking(spNetCon->nSd, 0); + } + } + + /* Try to connect to the other side. + */ + if(connect(spNetCon->nSd, (struct sockaddr *)&sServer, + sizeof(struct sockaddr)) == -1) + { +#if defined(_WIN32) + /* What was the error...? Under windows its bound to be bad news!!! + * Call windows own function (well blow me down and call me + * gandolfo) to liberate the error code. + */ + switch((nWinErr = WSAGetLastError())) +#endif +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) + /* What was the error..? Bad news...? + */ + switch(errno) +#endif + { + /* A previous connect on this socket has now completed, so get + * out and finish connection process. + */ + case EISCONN: + break; + + /* The connection process would block, but the underlying is + * still trying to connect. + */ + case EINPROGRESS: + case EWOULDBLOCK: + spNetCon->nStatus = SSL_DOWN; + Errno = E_BADCONNECT; + return(R_FAIL); + + /* Soft errors which may be resolved dynamically, so just back + * off for a while. + */ + case EADDRINUSE: + case EALREADY: + case EBADF: + case ECONNREFUSED: + case EINTR: + case ENOTSOCK: + case ETIMEDOUT: + case EINVAL: + case EIO: + /* Get rid of socket, no longer needed. + */ + SocketClose(spNetCon->nSd); + spNetCon->nSd = -1; + spNetCon->nStatus = SSL_DOWN; + Errno = E_NOCONNECT; + return(R_FAIL); + + /* Hard errors which cannot be resolved, mark the connection + * as down permanently. + */ + case EADDRNOTAVAIL: + case EAFNOSUPPORT: + case EFAULT: + case ENETUNREACH: + default: + /* Get rid of socket, no longer needed. + */ + SocketClose(spNetCon->nSd); + spNetCon->nSd = -1; + Lgr(LOG_WARNING, szFunc, + "Cannot connect (%s, %s, %d) Error (%d)", + spNetCon->szServerName, + SL_HostIPtoString(spNetCon->lServerIPaddr), + spNetCon->nServerPortNo, +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) + errno); +#endif +#if defined(_WIN32) + nWinErr); +#endif + spNetCon->nStatus = SSL_FAIL; + Errno = E_NOCONNECT; + return(R_FAIL); + } + } + + /* Set up KEEPALIVE, so the underlying keeps an eye on the net/ + * processes going up/down. + */ + if( setsockopt(spNetCon->nSd, SOL_SOCKET, SO_KEEPALIVE, + (UCHAR *)&Sl.nSockKeepAlive, + sizeof(Sl.nSockKeepAlive)) < 0 ) + { + Lgr(LOG_WARNING, szFunc, + "Couldnt set KEEPALIVE on socket (%d)", spNetCon->nSd); + } + + /* Set up LINGER to be disabled, if we die unexpectedly, the socket + * /ports should be freed up. We lose data, but nothing can be done. + */ + sLinger.l_onoff = 0; + sLinger.l_linger = 0; + if( setsockopt(spNetCon->nSd, SOL_SOCKET, SO_LINGER, (UCHAR *)&sLinger, + sizeof(struct linger)) < 0 ) + { + Lgr(LOG_WARNING, szFunc, + "Couldnt disable LINGER on socket (%d)", spNetCon->nSd); + } + + /* Finally, call the users control callback to let him + * know about the new connection. + */ + spNetCon->nCntrlCallback(SLC_CONNECT, spNetCon->nChanId, + _SL_GetPortNo(spNetCon), spNetCon->lServerIPaddr, + spNetCon->nOurPortNo); + + /* Mark connection as up. + */ + spNetCon->nStatus = SSL_UP; + + /* Finished, success, get out!! + */ + return( R_OK ); +} + +/****************************************************************************** + * Function: _SL_ReceiveFromSocket + * Description: Receive data from a given socket, growing the receive buffer + * if needed. + * Thread Safe: No, forces SL thread entry only. + * Returns: R_OK - + * R_FAIL - + * E_NOMEM - Memory exhaustion. + * E_BADPARM - Bad parameters passed to function. + * E_NOSERVICE - No service on socket. + ******************************************************************************/ +int _SL_ReceiveFromSocket( SL_NETCONS *spNetCon ) /* IO: Active connection */ +{ + /* Local variables. + */ + UINT nBufLen; + UINT nNdx; + int nRet; + int nReturn = R_FAIL; +#if defined(_WIN32) + int nWinErr; +#endif + char *szFunc = "_SL_ReceiveFromSocket"; + UCHAR *spNewBuf; + UCHAR *spTmp; + UCHAR *spTmp2; + + SL_THREAD_ONLY; + + /* Check parameters. + */ + if( spNetCon == NULL ) + { + Errno = E_BADPARM; + Lgr(LOG_DEBUG, szFunc, "spNetCon is null"); + return(nReturn); + } + + do { + /* Calculate number of bytes to read. + */ + if((nBufLen=(spNetCon->nRecvBufLen-spNetCon->nRecvLen)) > MAX_RECVLEN) + { + nBufLen = MAX_RECVLEN; + } + + /* Read as many bytes as possible from the active socket upto the + * size of the receive buffer. If more bytes are available, then + * grow the receive buffer to accomodate. + */ + nRet=recv(spNetCon->nSd, &(spNetCon->spRecvBuf[spNetCon->nRecvLen]), + nBufLen, 0); + + /* If data has been received, then check on the number of bytes read. + */ + if( nRet > 0 ) + { + /* Does the number of bytes read fill the buffer? + */ + if((nRet + spNetCon->nRecvLen) >= spNetCon->nRecvBufLen) + { + /* For safety's sake, an upper limit on the size of the + * receive buffer has to be implemented. If this ceiling + * is hit, then assume something is going wrong, so keep + * the current buffer, but dump the contents. + */ + if( spNetCon->nRecvBufLen >= MAX_RECVBUFSIZE ) + { + Lgr(LOG_WARNING, szFunc, + "Exceeded maximum size of recv buffer, dumping"); + spNetCon->nRecvLen = 0; + break; + } + + /* OK, lets grow the buffer by a fixed size. If no memory + * is available, then keep the current buffer, just stop + * reading. Later functionality will prune the data. + */ + if((spNewBuf=(UCHAR *)malloc(spNetCon->nRecvBufLen+ + DEF_BUFINCSIZE)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + DEF_BUFINCSIZE); + nRet = -1; + break; + } + + /* Copy the old buffer onto the new, so we can release it + * back to the sys pool. + */ + for(spTmp=spNetCon->spRecvBuf, spTmp2=spNewBuf, + nNdx=spNetCon->nRecvBufLen; + nNdx != 0; + nNdx--, *spTmp2 = *spTmp, spTmp++, spTmp2++); + + /* Get rid of old buffer, and update structure pointers + * to accomodate the new buffer and new size values. + */ + free(spNetCon->spRecvBuf); + spNetCon->spRecvBuf = spNewBuf; + spNetCon->nRecvBufLen += DEF_BUFINCSIZE; + + /* Log a message indicating that the buffer has grown in size. + */ + Lgr(LOG_DEBUG, szFunc, + "Allocated new receive buffer of %d bytes", + spNetCon->nRecvBufLen); + } + + /* Update the total number of bytes held in the receive buffer. + */ + spNetCon->nRecvLen += nRet; + nReturn = R_OK; + } + } while( nRet > 0 ); + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) + /* Check for errors. Errno should never be anything but EWOULDBLOCK. + */ + if(nRet < 1 && errno != EWOULDBLOCK) +#endif +#if defined(_WIN32) + /* Get the error code from windows to decide on our course of action. + */ + nWinErr = WSAGetLastError(); + + /* Test for errors. + */ + if(nRet < 1 && nWinErr != EWOULDBLOCK) +#endif + { + Errno = E_NOSERVICE; + nReturn = R_FAIL; + } + + /* Finished, get out!! + */ + return( nReturn ); +} + +/****************************************************************************** + * Function: _SL_ProcessRecvBuf + * Description: Process the data held in a network connection's receive buffer. + * If a complete packet has been assembled and passed a CRC check, + * pass the data to the subscribing application via its callback. + * Thread Safe: No, forces SL thread entry only. + * Returns: R_OK - + * R_FAIL - + * E_NOMEM - Memory exhaustion. + * E_NOSOCKET - Couldnt allocate a socket for connection. + ******************************************************************************/ +int _SL_ProcessRecvBuf( SL_NETCONS *spNetCon ) +{ + /* Local variables. + */ + int nReturn = R_OK; + UINT nTmpLen; + char *szFunc = "_SL_ProcessRecvBuf"; + UCHAR *spTmp; + + SL_THREAD_ONLY; + + if(spNetCon->nRawMode == FALSE) + { + do { + /* Go through the receive buffer and look for two SYNch characters + * followed by a start of message (STX), this identifies the start + * of a data packet. Once found, extract the two preceeding bytesi + * which indicate the total length of the data packet. + */ + for(spTmp=spNetCon->spRecvBuf, nTmpLen=0; + (spTmp+5) < (spNetCon->spRecvBuf+spNetCon->nRecvLen); + spTmp++, nTmpLen=0) + { + /* Start of packet? + */ + if( *(spTmp+0) == A_SYN && + *(spTmp+1) == A_SYN && + *(spTmp+2) == A_STX ) + { + /* Get length. + */ + nTmpLen = GetIntFromChar(spTmp+3); + + /* If there are not enough bytes in the buffer, then loop, + * because this probably isnt a valid packet or we havent + * yet received the packet in its entirety. + */ + if((spTmp+nTmpLen+7) <= + (spNetCon->nRecvLen+spNetCon->spRecvBuf) + + /* Using the length, take a peek at the end of the data + * packet and establish that it is a valid packet by the + * presence of an end of message (ETX). + */ + && *(spTmp+5+nTmpLen) == A_ETX + + /* Finally, check to see if the CRC on the data within the + * packet matches that stored within it. + */ + && _SL_CheckCRC(spTmp+5, nTmpLen+2) == R_OK ) + { + break; + } + } + } + + /* If we have isolated a packet, then call the data callback to + * process it. + */ + if(nTmpLen > 0) + { + /* Execute the callback function with the obtained data. + */ + if(spNetCon->nDataCallback != NULL) + { + spNetCon->nDataCallback(spNetCon->nChanId,spTmp+5,nTmpLen); + } else + { + Lgr(LOG_DEBUG, szFunc, + "Data arriving on a channel (%d) with no handler", + spNetCon->nChanId); + } + + /* Finally, shift data up in the buffer from the end of the last + * byte of the packet we've just processed. + */ + spNetCon->nRecvLen -= ((spTmp+nTmpLen+8) - spNetCon->spRecvBuf); + if(spNetCon->nRecvLen > 0) + memcpy(spNetCon->spRecvBuf, spTmp+nTmpLen+8, + spNetCon->nRecvLen); + } + } while(nTmpLen > 0); + } else + { + /* Execute the callback function with all the data in the buffer. + */ + if(spNetCon->nDataCallback != NULL) + { + spNetCon->nDataCallback(spNetCon->nChanId, spNetCon->spRecvBuf, + spNetCon->nRecvLen); + spNetCon->nRecvLen = 0; + } else + { + Lgr(LOG_DEBUG, szFunc, + "Data arriving on a channel (%d) with no handler", + spNetCon->nChanId); + } + } + + /* Finished, get out!! + */ + return( nReturn ); +} + +/****************************************************************************** + * Function: _SL_ProcessWaitingPorts + * Description: + * Thread Safe: No, forces SL Thread only. + * Returns: R_OK - Select succeeded. + * R_FAIL - Catastrophe, see Errno. + * E_BADSELECT - Internal failure causing select to fail. + * E_NONWAITING - No sockets waiting processing. + ******************************************************************************/ +int _SL_ProcessWaitingPorts( ULNG nHibernationPeriod ) /* I: Select sleep*/ +{ + /* Local variables. + */ + int nReturn = R_FAIL; + int nStatus; + ULNG lCurrTimeMs; + fd_set ReadList; + fd_set WriteList; + fd_set ExceptList; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + struct timeb sTp; + struct timeval sTimeDelay; + char *szFunc = "_SL_ProcessWaitingPorts"; + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) + pid_t nPid; + SL_NETCONS *spNewClnt; +#endif + + SL_THREAD_ONLY; + + /* Zap select lists, only interested in our own Sockets. + */ + FD_ZERO(&ReadList); + FD_ZERO(&WriteList); + FD_ZERO(&ExceptList); + + /* Get current time to validate comms down timers. + */ + ftime(&sTp); + lCurrTimeMs = (sTp.time * 1000L) + (ULNG)sTp.millitm; + + /* Scan list and enable all read/write/except flags on active sockets. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); spNetCon != NULL; + spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + /* Connection still waiting to be connected to server? + */ + if(spNetCon->nStatus == SSL_DOWN && + spNetCon->cCorS == STP_CLIENT && + spNetCon->lDownTimer < lCurrTimeMs) + { + /* If connection fails, set down timer, so we dont bother re-trying + * to process for a while. + */ + if(_SL_ConnectToServer(spNetCon) == R_FAIL) + { + switch(Errno) + { + case E_BADCONNECT: + spNetCon->lDownTimer = lCurrTimeMs + DEF_CONWAITPER; + break; + + default: + case E_NOCONNECT: + spNetCon->lDownTimer = lCurrTimeMs + DEF_CONFAILPER; + break; + } + } + } + + /* Server port still listening? See if any inbounds are waiting. + */ + if(spNetCon->nStatus == SSL_LISTENING) + { + FD_SET(spNetCon->nSd, &ReadList); + } + + /* Active connections? Need to know if they are ready to accept + * data or have data awaiting. + */ + if(spNetCon->nStatus == SSL_UP) + { + FD_SET(spNetCon->nSd, &ReadList); + FD_SET(spNetCon->nSd, &WriteList); + } + + /* If there is data which is awaiting xmission, then try to send it. + */ + if(spNetCon->nStatus == SSL_UP && spNetCon->spXmitBuf != NULL) + { + SL_SendData(spNetCon->nChanId, NULL, 0); + } + } + + /* Issue select on ports of interest, should return immediately or after + * the programmed delay, thereby not blocking action for too long. + */ + sTimeDelay.tv_sec = nHibernationPeriod/1000; + nHibernationPeriod -= sTimeDelay.tv_sec * 1000; + sTimeDelay.tv_usec = (nHibernationPeriod * 1000L); + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) + nStatus=select(getdtablesize(), &ReadList, NULL, NULL, &sTimeDelay); +#endif +#if defined(_WIN32) + nStatus=select(MAX_WIN_RLIMIT, &ReadList, NULL, NULL, &sTimeDelay); +#endif + + if(nStatus > 0) + { + /* Go through lists and process any pending server connections, data + * for reception or transmit buffer waiting sessions. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); + spNetCon != NULL; + spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + /* Dont process any inactive ports. + */ + if(spNetCon->nStatus == SSL_FAIL || spNetCon->nStatus == SSL_DOWN) + continue; + + /* If the read bit is set, receive all data from the socket and + * store in internal buffer, ready for processing. + */ + if(FD_ISSET(spNetCon->nSd, &ReadList)) + { + /* Is this a listening device.... server port? + */ + if(spNetCon->nStatus == SSL_LISTENING) + { +#if defined(SOLARIS) || defined(LINUX) || defined(SUNOS) || defined(ZPU) + /* If the option to Fork on a new connection has been set, + * then fork a child and let it perform the accept of the + * incoming connections. + */ + if(spNetCon->nForkForAccept == TRUE) + { + /* Accept the connection prior to child fork. + */ + if(_SL_AcceptClient(spNetCon->nSd, spNetCon, &spNewClnt) + == R_OK) + { + /* Fork child to handle new connection. + */ + if((nPid=fork()) < 0) + { + Lgr(LOG_DEBUG, szFunc, + "Couldnt fork a new process, will retry.."); + } else + /* If we are the child then close the parent service + * port as we are not interested in it. + */ + if(nPid == 0) + { + _SL_Close(spNetCon, FALSE); + } else + /* If we are the parent then close the accepted + * child socket. + */ + { + _SL_Close(spNewClnt, FALSE); + fflush(stdout); + } + } + } else + { + /* For standard comms, just accept the connection. + * No need to worry about forking etc. + */ + _SL_AcceptClient(spNetCon->nSd, spNetCon, NULL); + } +#endif + +#if defined(_WIN32) + /* For standard comms, just accept the connection. + * No need to worry about forking etc. + */ + _SL_AcceptClient(spNetCon->nSd, spNetCon, NULL); +#endif + } else + { + if(_SL_ReceiveFromSocket(spNetCon) == R_OK) + { + /* See if a full packet has been assembled. + */ + _SL_ProcessRecvBuf(spNetCon); + } else + { + /* Process any remaining valid packets prior to + * exception handling. + */ + _SL_ProcessRecvBuf(spNetCon); + + /* Indicate that an exception has occurred on + * this socket. + */ + if(Errno == E_NOSERVICE) + FD_SET(spNetCon->nSd, &ExceptList); + } + } + } + + /* Any exceptions occurred on a socket? + */ + if(FD_ISSET(spNetCon->nSd, &ExceptList)) + { + /* If this is a server then delete the connection. + */ + if(spNetCon->cCorS == STP_SERVER) + { + /* Close the connection as it is no longer required. + */ + _SL_Close(spNetCon, TRUE); + } else + { + /* A client failure just requires the link to be marked + * down and it will eventually be rebuilt. + */ + spNetCon->nStatus = SSL_DOWN; + spNetCon->nCntrlCallback(SLC_LINKDOWN, spNetCon->nChanId, + _SL_GetPortNo(spNetCon), + spNetCon->lServerIPaddr); + } + } + + /* This Channel marked for closure? Close it only if all data + * for transmission has been sent. + */ + if(spNetCon->nClose == TRUE && spNetCon->spXmitBuf == NULL) + { + _SL_Close(spNetCon, TRUE); + } + } + nReturn = R_OK; + } else + { + if(nStatus == 0) + { + nReturn = R_OK; + } else + { + printf("Bad Select (%d)\n", nStatus); + fflush(stdout); + Errno = E_BADSELECT; + } + } + +#if defined(SUNOS) || defined(LINUX) || defined(ZPU) + /* Addition: 26/5/1996. Soak up all child result codes for fork on accept + * children. + */ + wait4(0, &nStatus, WNOHANG, NULL); +#endif +#if defined(SOLARIS) + /* Addition: 4/9/1996. Soak up all child result codes for fork on accept + * children. + */ + wait3(&nStatus, WNOHANG|WUNTRACED, NULL); +#endif + + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: _SL_ProcessCallbacks + * Description: Activate any callbacks whose timers are active and have + * expired. + * Thread Safe: No, only allows SL Thread. + * Returns: Time in mS till next callback. + * + ******************************************************************************/ +ULNG _SL_ProcessCallbacks( void ) +{ + /* Local variables. + */ + ULNG nReturn = DEF_MAXBLOCKPERIOD; + ULNG lCurrTimeMs; + LINKLIST *spNext; + SL_CALLIST *spCB; + struct timeb sTp; + + SL_THREAD_ONLY; + + /* Get current time to set any new expiration timers. + */ + ftime(&sTp); + lCurrTimeMs = (sTp.time * 1000L) + (ULNG)sTp.millitm; + + /* Go through callback list and invoke callback function if timer has + * expired. + */ + for(spCB=(SL_CALLIST *)StartItem(Sl.spCBHead, &spNext); spCB != NULL; + spCB=(SL_CALLIST *)NextItem(&spNext)) + { + /* If the callback is active, and its timer has expired, execute + * the callback. + */ + if( spCB->nStatus == TCB_UP && spCB->lTimeExpire <= lCurrTimeMs ) + { + if( spCB->nCallback != NULL ) + spCB->nCallback(spCB->lCBData); + + /* Update counter according to options flag. + */ + switch(spCB->nOptions) + { + case TCB_ASTABLE: + /* Astable means that the time is equidistant from + * the last. + */ + spCB->lTimeExpire = lCurrTimeMs + spCB->lTimePeriod; + break; + + case TCB_FLIPFLOP: + /* Re-read time, as flip flop commences from whence the + * function completed. + */ + ftime(&sTp); + lCurrTimeMs = (sTp.time * 1000L) + (ULNG)sTp.millitm; + spCB->lTimeExpire = lCurrTimeMs + spCB->lTimePeriod; + break; + + case TCB_ONESHOT: + default: + spCB->nStatus = TCB_DOWN; + break; + } + } + + /* If this is an active callback, then see if its expiration time + * is the lowest. The lowest time period is used for the select + * hibernation time. + */ + if( spCB->nStatus == TCB_UP && + nReturn > (spCB->lTimeExpire - lCurrTimeMs) ) + { + nReturn = (spCB->lTimeExpire - lCurrTimeMs); + } + } + + /* Return result code to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: SL_HostIPtoString + * Description: Convert a given IP address into a string dot notation. + * Thread Safe: No, API only allows one thread at a time. + * Returns: 16bit CRC + ******************************************************************************/ +UCHAR *SL_HostIPtoString( ULNG lIPaddr ) /* I: IP address to convert */ +{ + /* Statics and local variables. + */ + static UCHAR szIPaddr[16]; + UCHAR szTmpBuf[5]; + + SL_SINGLE_THREAD_ONLY; + + /* Convert long into 4 bytes. + */ + PutCharFromLong(szTmpBuf, lIPaddr); + + /* Copy into string. + */ + sprintf(szIPaddr, "%d%c%d%c%d%c%d", + (UINT)szTmpBuf[0], '.', + (UINT)szTmpBuf[1], '.', + (UINT)szTmpBuf[2], '.', + (UINT)szTmpBuf[3]); + + /* Return address of string to caller. + */ + SL_SINGLE_THREAD_EXIT(szIPaddr); +} + +/****************************************************************************** + * Function: SL_GetIPaddr + * Description: Get the Internet address of the local machine or a named + * machine. + * Thread Safe: No, API only allows one thread at a time. + * Returns: R_OK - IP address obtained. + * R_FAIL - IP address not obtained. + ******************************************************************************/ +int SL_GetIPaddr( UCHAR *szHost, /* I: Hostname string */ + ULNG *lIPaddr ) /* O: Storage for the Internet Addr */ +{ + /* Local variables. + */ + int nReturn = R_FAIL; + char szHostName[MAX_MACHINENAME+1]; + struct hostent *spHostEnt; + + SL_SINGLE_THREAD_ONLY; + + /* If no hostname provided, use local machine name. + */ + if(szHost == NULL) + { + /* Get host name from /etc/hosts. + */ + gethostname(szHostName, MAX_MACHINENAME); + } else + { + strcpy(szHostName, szHost); + } + + if( (spHostEnt=gethostbyname( szHostName )) != NULL ) + { + if( spHostEnt->h_addrtype == AF_INET ) + { + /* This is cheating a little, as Im assuming in_addr will + * always be 32 bit. + */ + memcpy((UCHAR *)lIPaddr, spHostEnt->h_addr, 4); + *lIPaddr = ntohl(*lIPaddr); + nReturn = R_OK; + } + } + + /* Finished, get out!! + */ + SL_SINGLE_THREAD_EXIT( nReturn ); +} + +/****************************************************************************** + * Function: SL_GetService + * Description: Get the TCP/UDP service port number from the Services File. + * Thread Safe: No, API Function only allows one thread at a time. + * Returns: R_OK - Service port obtained. + * R_FAIL - Service port not obtained. + ******************************************************************************/ +int SL_GetService( UCHAR *szService, /* I: Service Name string */ + UINT *nPortNo ) /* O: Storage for the Port Number */ +{ + /* Local variables. + */ + int nReturn = R_FAIL; + struct servent *spServiceEnt; + + SL_SINGLE_THREAD_ONLY; + + /* Call unix functions to search the services file/database for the + * requested service. + */ + if( (spServiceEnt=getservbyname( szService, NULL )) != NULL ) + { + /* Setup callers variable with the service port number. + */ + *nPortNo = ntohs(spServiceEnt->s_port); + nReturn = R_OK; + } + + /* Finished, get out!! + */ + SL_SINGLE_THREAD_EXIT( nReturn ); +} + +/****************************************************************************** + * Function: SL_Init + * Description: Initialise communication variables and connect or setup + * listening for required socket connections. + * Thread Safe: No, API function only allows one thread at a time. + * Returns: R_OK - Comms functionality initialised. + * R_FAIL - Initialisation failed, see Errno. + * E_NOMEM - Memory exhaustion. + ******************************************************************************/ +int SL_Init( UINT nSockKeepAlive, /* I: Socket keep alive time period */ + UCHAR *szErrMsg ) /* O: Error message buffer */ +{ + /* Local variables. + */ + int nReturn = R_OK; + + SL_SINGLE_THREAD_ONLY; + +#if defined(_WIN32) + WSADATA WSAData; + + if(WSAStartup(MAKEWORD(1,1), &WSAData) != 0) + { + sprintf(szErrMsg, "Failed to initialise Windows Socket API"); + SL_SINGLE_THREAD_EXIT(R_FAIL); + } +#endif + + /* Save configuration information. + */ + Sl.nSockKeepAlive = nSockKeepAlive; + + /* Initialise all variables as needed. + */ + Sl.spHead = NULL; + Sl.spTail = NULL; + Sl.spCBHead = NULL; + Sl.spCBTail = NULL; + + /* Finished, get out!! + */ + SL_SINGLE_THREAD_EXIT( nReturn ); +} + +/****************************************************************************** + * Function: SL_Exit + * Description: Decommission the Comms module ready for program termination + * or re-initialisation. + * Thread Safe: No, API function, only allows one thread at a time. + * Returns: R_OK - Exit succeeded. + * R_FAIL - Couldnt perform exit processing, see errno. + * + ******************************************************************************/ +int SL_Exit( UCHAR *szErrMsg ) /* O: Error message buffer */ +{ + /* Local variables. + */ + int nReturn = R_OK; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + + SL_SINGLE_THREAD_ONLY; + + /* Free up network connection receive buffer memory. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); spNetCon != NULL; + spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + if(spNetCon->spRecvBuf != NULL) + free(spNetCon->spRecvBuf); + if(spNetCon->spXmitBuf != NULL) + free(spNetCon->spXmitBuf); + } + + /* Free up linked list memory. + */ + if(Sl.spHead != NULL) DelList(&Sl.spHead, &Sl.spTail); + if(Sl.spCBHead != NULL) DelList(&Sl.spCBHead, &Sl.spCBTail); + + /* Free up any character buffers... + */ + + /* Finished, get out!! + */ + SL_SINGLE_THREAD_EXIT( nReturn ); +} + +/****************************************************************************** + * Function: SL_PostTerminate + * Description: Post a request for the program to terminate as soon as + * possible. + * Thread Safe: Yes + * Returns: Non. + ******************************************************************************/ +void SL_PostTerminate( void ) +{ + /* Local variables. + */ + + /* To terminate, simply set the flag nCloseDown. + */ + Sl.nCloseDown = TRUE; + + /* Get out and let the wheels of the Kernel close us down. + */ + return; +} + +/****************************************************************************** + * Function: SL_GetChanId + * Description: Get a channel Id from a given IP addr. If the IP addr doesnt + * exist or is still pending a connection, return 0 as an error. + * Thread Safe: No, API function only allows one thread at a time. + * Returns: > 0 - Channel Id associated with IP address. + * 0 - Couldnt find an associated channel. + ******************************************************************************/ +UINT SL_GetChanId( ULNG lIPaddr ) /* I: Address to xlate */ +{ + /* Local variables. + */ + UINT nChanId = 0; + SL_NETCONS *spNetCon; + LINKLIST *spNext; + + SL_SINGLE_THREAD_ONLY; + + /* Go through list of client/server connections and see if we can obtain + * an IP address match. If a match occurs, then see if the Channel Id + * is valid. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); + spNetCon != NULL; + spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + if(spNetCon != NULL && spNetCon->lServerIPaddr == lIPaddr) + { + /* Consider the channel id to be valid if its active (UP) or + * temporarily out-of-service (DOWN). + */ + if( spNetCon->nStatus == SSL_DOWN || spNetCon->nStatus == SSL_UP ) + { + nChanId = spNetCon->nChanId; + } + break; + } + } + + /* Finished, get out!! + */ + SL_SINGLE_THREAD_EXIT( nChanId ); +} + +/****************************************************************************** + * Function: SL_RawMode + * Description: Function to switch a channel into/out of raw mode processing. + * Raw mode processing foregoes all forms of checking and is + * typically used for connections with a non SL lib server/client. + * Thread Safe: No, API Function, only allows one thread at a time. + * Returns: R_FAIL - Illegal Channel Id given. + * R_OK - Mode set + * + ******************************************************************************/ +int SL_RawMode( UINT nChanId, /* I: Channel to apply change to */ + UINT nMode ) /* I: Mode to set channel to */ +{ + /* Local variables. + */ + int nReturn = R_FAIL; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + + SL_SINGLE_THREAD_ONLY; + + /* Scan list to find corresponding channel information. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); spNetCon != NULL; + spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + /* Find the greatest Channel Id. + */ + if(spNetCon != NULL && spNetCon->nChanId == nChanId) + break; + } + + /* Did we find it? + */ + if(spNetCon != NULL) + { + spNetCon->nRawMode = (nChanId == 0 ? FALSE : TRUE); + nReturn = R_OK; + } + + /* Return result code to caller. + */ + SL_SINGLE_THREAD_EXIT(nReturn); +} + +/****************************************************************************** + * Function: SL_AddServer + * Description: Add an entry into the Network Connections table as a Server. + * An entry is built up and a socket created and set listening. + * Thread Safe: No, API Function, only allows one thread at a time. + * Returns: R_OK - Successfully added. + * R_FAIL - Error, see Errno. + * E_NOMEM - Memory exhaustion. + * E_BADPARM - Bad parameters passed. + * E_EXISTS - Entry already exists. + * E_NOSOCKET - Couldnt grab a socket. + * E_NOLISTEN - Couldnt listen on given port. + ******************************************************************************/ +int SL_AddServer( UINT nPortNo, /* I: Port to listen on */ + UINT nForkForAccept, /* I: Fork prior to accept */ + void (*nDataCallback)(), /* I: Data ready callback */ + void (*nCntrlCallback)(int, ...) ) /* I: Control callback */ +{ + /* Local variables. + */ + int nReturn = R_FAIL; + char *szFunc = "SL_AddServer"; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + struct sockaddr_in sServer; + + SL_SINGLE_THREAD_ONLY; + + /* Scan list to see if an entry exists for requested server, if it does + * then just exit. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); spNetCon != NULL; + spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + if(spNetCon != NULL && spNetCon->cCorS == STP_SERVER && + spNetCon->nOurPortNo == nPortNo) + { + Errno = E_EXISTS; + SL_SINGLE_THREAD_EXIT(nReturn); + } + } + + /* Create a Network Connection record, populate, set in motion and add + * to the support lists. + */ + if((spNetCon=(SL_NETCONS *)malloc(sizeof(SL_NETCONS))) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + sizeof(SL_NETCONS)); + Errno = E_NOMEM; + } else + { + /* Wash the new memory, just in case. + */ + memset((UCHAR *)spNetCon, '\0', sizeof(SL_NETCONS)); + + /* Fill out the remaining structure conflab. + */ + spNetCon->cCorS = STP_SERVER; + spNetCon->nClose = FALSE; + spNetCon->nOurPortNo = nPortNo; + spNetCon->nDataCallback = nDataCallback; + spNetCon->nCntrlCallback = nCntrlCallback; + spNetCon->nRecvBufLen = DEF_INITRECVBUF; + spNetCon->nStatus = SSL_LISTENING; + spNetCon->nForkForAccept = nForkForAccept; + spNetCon->spXmitBuf = NULL; + spNetCon->nXmitLen = 0; + spNetCon->nXmitPos = 0; + + /* Build up Server address info, so it can be publicised by bind to + * the big wide world. + */ + memset((UCHAR *)&sServer, '\0', sizeof(struct sockaddr)); + sServer.sin_family = AF_INET; + sServer.sin_port = htons((USHRT)spNetCon->nOurPortNo); + sServer.sin_addr.s_addr = htonl(INADDR_ANY); + + /* Fire up a socket and lets listen. + */ + if((spNetCon->nSd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + Errno = E_NOSOCKET; + free(spNetCon->spRecvBuf); + free(spNetCon); + } else + if(bind(spNetCon->nSd, (struct sockaddr *)&sServer, + sizeof(struct sockaddr)) == -1) + { + Errno = E_NOBIND; + free(spNetCon->spRecvBuf); + free(spNetCon); + } else + if(listen(spNetCon->nSd, MAX_SOCKETBACKLOG) == -1) + { + Errno = E_NOLISTEN; + free(spNetCon->spRecvBuf); + free(spNetCon); + } else + { + /* OK, almost there, now will it stick onto the lists!!? + */ + if(AddItem(&Sl.spHead, &Sl.spTail, SORT_NONE, &spNetCon->nChanId, + &spNetCon->lServerIPaddr, spNetCon->szServerName, + spNetCon) == R_OK) + { + nReturn = R_OK; + } else + { + /* Free used memory, Errno already set by AddItem. + */ + free(spNetCon); + } + } + } + + /* Return Channel ID or error to caller. + */ + SL_SINGLE_THREAD_EXIT(nReturn); +} + +/****************************************************************************** + * Function: SL_AddClient + * Description: Add an entry into the Network Connections table as a client. + * Socket creation and connect are left to the kernels + * discretion. + * Thread Safe: No, API function, only allows one thread at a time. + * Returns: >= 0 - Channel Id. + * -1 - Error, see Errno. + * E_NOMEM - Memory exhaustion. + * E_EXISTS - A client of same detail exists. + ******************************************************************************/ +int SL_AddClient( UINT nServerPortNo, /* I: Server port to talk on */ + ULNG lServerIPaddr, /* I: Server IP address */ + UCHAR *szServerName, /* I: Name of Server */ + void (*nDataCallback)(), /* I: Data ready callback */ + void (*nCntrlCallback)(int, ...) ) /* I: Control callback */ +{ + /* Local variables. + */ + UINT nChanId = DEF_CHANID; + int nReturn = -1; + char *szFunc = "SL_AddClient"; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + + SL_SINGLE_THREAD_ONLY; + + /* Scan list to see if an entry exists for requested client, if it does + * then just exit. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); spNetCon != NULL; + spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + /* Find the greatest Channel Id. + */ + if(spNetCon != NULL && spNetCon->nChanId > nChanId) + nChanId = spNetCon->nChanId; + + /* Duplicate? + if(spNetCon != NULL && spNetCon->cCorS == STP_CLIENT && + spNetCon->nServerPortNo == nServerPortNo && + spNetCon->lServerIPaddr == lServerIPaddr ) + { + Errno = E_EXISTS; + SL_SINGLE_THREAD_EXIT(nReturn); + } + */ + } + + /* Create a Network Connection record, populate, see if a connection with + * the remote server can be obtained, then add to the support lists. + */ + if((spNetCon=(SL_NETCONS *)malloc(sizeof(SL_NETCONS))) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + sizeof(SL_NETCONS)); + Errno = E_NOMEM; + } else + { + /* Wash the new memory, just in case. + */ + memset((UCHAR *)spNetCon, '\0', sizeof(SL_NETCONS)); + + /* Try and allocate an initial receive buffer. + */ + if((spNetCon->spRecvBuf=(UCHAR *)malloc(DEF_INITRECVBUF)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + DEF_INITRECVBUF); + Errno = E_NOMEM; + free(spNetCon); + } else + { + /* Fill out the remaining structure conflab. + */ + spNetCon->cCorS = STP_CLIENT; + spNetCon->nSd = -1; + spNetCon->nRawMode = FALSE; + spNetCon->nServerPortNo = nServerPortNo; + spNetCon->lServerIPaddr = lServerIPaddr; + strcpy(spNetCon->szServerName, szServerName); + spNetCon->nDataCallback = nDataCallback; + spNetCon->nCntrlCallback = nCntrlCallback; + spNetCon->nRecvBufLen = DEF_INITRECVBUF; + spNetCon->nStatus = SSL_DOWN; + spNetCon->nChanId = nChanId + 1; + spNetCon->lDownTimer = 0L; + spNetCon->spXmitBuf = NULL; + spNetCon->nXmitLen = 0; + spNetCon->nXmitPos = 0; + + /* OK, almost there, now will it stick onto the lists!!? + */ + if(AddItem(&Sl.spHead, &Sl.spTail, SORT_NONE, &spNetCon->nChanId, + &spNetCon->lServerIPaddr, spNetCon->szServerName, + spNetCon) == R_OK) + { + nReturn = spNetCon->nChanId; + } else + { + /* Free up used memory, Errno has been set by AddItem. + */ + free(spNetCon->spRecvBuf); + free(spNetCon); + } + } + } + + /* Return Channel ID or error to caller. + */ + SL_SINGLE_THREAD_EXIT(nReturn); +} + +/****************************************************************************** + * Function: SL_AddTimerCB + * Description: Add a timed callback. Basically, a timed callback is a function + * which gets invoked after a period of time. This function + * can be invoked once (TCB_ONESHOT), every Xms (TCB_ASTABLE) or + * a fixed period of time Xms from last execution (TCB_FLIPFLOP). + * Each callback can pass a predefined variable/pointer, so + * multiple instances of the same callback can exist, each + * referring to the same function, but passing different values + * to it. The callbacks are maintained in a dynamic link list. + * Thread Safe: No, API Function, only allows single thread at a time. + * Returns: R_OK - Callback added successfully. + * R_FAIL - Failure, see Errno. + * E_NOMEM - Memory exhaustion. + ******************************************************************************/ +int SL_AddTimerCB( ULNG lTimePeriod, /* I: Time between callbacks */ + UINT nOptions, /* I: Option flags on callback */ + ULNG lCBData, /* I: Data to be passed to cb */ + void (*nCallback)() ) /* I: Function to call */ +{ + /* Local variables. + */ + int nReturn = R_FAIL; + ULNG lCurrTimeMs; + char *szFunc = "SL_AddTimerCB"; + SL_CALLIST *spCB; + LINKLIST *spNext; + struct timeb sTp; + + SL_SINGLE_THREAD_ONLY; + + /* Go through callback list to locate an existing entry. Existing entries + * may occur as the application is just updating the configuration of + * the given callback. + */ + for(spCB=(SL_CALLIST *)StartItem(Sl.spCBHead, &spNext); spCB != NULL; + spCB=(SL_CALLIST *)NextItem(&spNext)) + { + /* If the callback is the same... and the data back is the same, then + * we are updating an existing record. + */ + if( spCB->nCallback == nCallback && spCB->lCBData == lCBData ) + break; + } + + /* Does a current entry exist? If not, allocate and add to linked list. + */ + if( spCB == NULL ) + { + /* Create record. + */ + if((spCB = (SL_CALLIST *)malloc(sizeof(SL_CALLIST))) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + sizeof(SL_CALLIST)); + Errno = E_NOMEM; + } else + { + /* Wash the new memory, just in case. + */ + memset((UCHAR *)spCB, '\0', sizeof(SL_CALLIST)); + } + + /* Add to callback list. + */ + if(AddItem(&Sl.spCBHead, &Sl.spCBTail, SORT_NONE, NULL, NULL, NULL, + spCB) == R_FAIL) + { + /* Dont modify Errno as AddItem has already set it for the + * correct error condition. + */ + free(spCB); + spCB = NULL; + } else + { + /* Store the callback address, no need to accomplish in an update + * scenario. + */ + spCB->nCallback = nCallback; + } + } + + /* Still ok...? ie Where in an update situation or we successfully + * allocated, initialised and linked in a new record! + */ + if( spCB != NULL ) + { + /* Get current time to set expiration timer. + */ + ftime(&sTp); + lCurrTimeMs = (sTp.time * 1000L) + (ULNG)sTp.millitm; + + /* Copy in/update the required parameters. + */ + if((spCB->nOptions = nOptions) == TCB_OFF) + { + spCB->nStatus = TCB_DOWN; + } else + { + spCB->lTimePeriod = lTimePeriod; + spCB->lCBData = lCBData; + spCB->lTimeExpire = lCurrTimeMs + lTimePeriod; + spCB->nStatus = TCB_UP; + } + nReturn = R_OK; + } + + /* Return result code to caller. + */ + SL_SINGLE_THREAD_EXIT(nReturn); +} + +/****************************************************************************** + * Function: SL_DelServer + * Description: Delete an entry from the Network Connections table and + * disable the actual communications associated with it. + * Thread Safe: No, API function allows one thread at a time. + * Returns: R_OK - Successfully deleted. + * R_FAIL - Error, see Errno. + * E_BADPARM - Bad parameters passed. + ******************************************************************************/ +int SL_DelServer( UINT nPortNo ) /* I: Port number that server on */ +{ + /* Local variables. + */ + int nReturn = R_FAIL; + char *szFunc = "SL_DelServer"; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + + SL_SINGLE_THREAD_ONLY; + + /* Scan list to find the required entry for deletion. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); spNetCon != NULL; + spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + if(spNetCon != NULL && spNetCon->cCorS == STP_SERVER && + spNetCon->nOurPortNo == nPortNo) + { + /* Entry found, so close it down. + */ + nReturn=_SL_Close(spNetCon, FALSE); + + /* Exit with result code. + */ + SL_SINGLE_THREAD_EXIT(nReturn); + } + } + + /* Didnt find the entry so exit with fail. + */ + SL_SINGLE_THREAD_EXIT(nReturn); +} + +/****************************************************************************** + * Function: SL_DelClient + * Description: Delete a client entry from the Network Connections table and + * free up all resources that it used. + * Thread Safe: No, API function allows on thread at a time. + * Returns: R_OK - Client connection successfully closed. + * R_FAIL - Error, see Errno. + * + ******************************************************************************/ +int SL_DelClient( UINT nChanId ) /* I: Channel Id of client to del*/ +{ + /* Local variables. + */ + int nReturn = R_FAIL; + char *szFunc = "SL_DelClient"; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + + SL_SINGLE_THREAD_ONLY; + + /* Scan list to see if an entry exists for requested client, if it does + * then delete it. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); spNetCon != NULL; + spNetCon=(SL_NETCONS *)NextItem(&spNext)) + { + /* If we have a match on channel Id then we have located the correct + * entry. + */ + if(spNetCon != NULL && /* spNetCon->cCorS == STP_CLIENT &&*/ + spNetCon->nChanId == nChanId ) + { + /* Entry found, so close it down. + */ + nReturn=_SL_Close(spNetCon, FALSE); + + /* Exit with result code. + */ + SL_SINGLE_THREAD_EXIT(nReturn); + } + } + + /* Didnt find the required entry so exit with failure code. + */ + SL_SINGLE_THREAD_EXIT(nReturn); +} + +/****************************************************************************** + * Function: SL_Close + * Description: Close a socket connection (Client or Server) based on the + * given channel ID. Due to the nature of the socket library, + * this cannot be performed immediately, as nearly always, the + * application requesting the close is within a socket library + * callback, and modifying internal structures in this state is + * fraught with danger. + * Thread Safe: No, API function, only allows one thread at a time. + * Returns: Non. + ******************************************************************************/ +int SL_Close( UINT nChanId ) /* I: Channel Id to close */ +{ + /* Local variables. + */ + char *szFunc = "SL_Close"; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + + SL_SINGLE_THREAD_ONLY; + + /* Using the channel Id, seek out the corresponding Net Connection + * record. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); + spNetCon != NULL && spNetCon->nChanId != nChanId; + spNetCon=(SL_NETCONS *)NextItem(&spNext)); + + /* If we cant locate a record corresponding to the given channel Id, + * then the calling application has passed us a bad value or internal + * workings are going wrong. + */ + if(spNetCon != NULL && spNetCon->nChanId != nChanId) + { + Errno = E_INVCHANID; + SL_SINGLE_THREAD_EXIT(R_FAIL); + } + + /* OK, record found, mark the channel for closure and get out. + */ + spNetCon->nClose = TRUE; + + /* Get out, big bang time! + */ + SL_SINGLE_THREAD_EXIT(R_OK); +} + +/****************************************************************************** + * Function: SL_SendData + * Description: Transmit a packet of data to a given destination identified + * by it channel Id. + * Thread Safe: No, API function, only allows one thread at a time. + * Returns: R_OK - Data sent successfully. + * R_FAIL - Couldnt send data, see Errno. + * E_INVCHANID - Invalid channel Id. + * E_BUSY - Channel is busy, retry later. + * E_BADSOCKET - Internal failure on socket, terminal. + * E_NOSERVICE - No remote connection established yet. + ******************************************************************************/ +int SL_SendData( UINT nChanId, /* I: Channel Id to send data on */ + UCHAR *szData, /* I: Data to be sent */ + UINT nDataLen ) /* I: Length of data */ +{ + /* Local variables. + */ + UINT nDataCRC; + int nNewBuf = FALSE; + int nReturn = R_FAIL; + int nSend; +#if defined(_WIN32) + int nWinErr; +#endif + char *szFunc = "SL_SendData"; + LINKLIST *spNext; + SL_NETCONS *spNetCon; + + SL_SINGLE_THREAD_ONLY; + + /* Scan list to see if an entry exists for requested channel. + */ + for(spNetCon=(SL_NETCONS *)StartItem(Sl.spHead, &spNext); + spNetCon != NULL && spNetCon->nChanId != nChanId; + spNetCon=(SL_NETCONS *)NextItem(&spNext)); + + /* If the channel is invalid, get out. + */ + if(spNetCon == NULL) + { + Errno = E_INVCHANID; + SL_SINGLE_THREAD_EXIT(nReturn); + } + + /* If the caller has passed no data in then he is wanting to flush any + * existing buffer out and get a result from it. If there is no data + * pending for transmission then exit with OK. + */ + if(szData == NULL && spNetCon->spXmitBuf == NULL) + { + SL_SINGLE_THREAD_EXIT(R_OK); + } + + /* If the caller wants to send a buffer but a previous buffer is still + * pending transmission, then exit with busy. + */ + if(szData != NULL && spNetCon->spXmitBuf != NULL) + { + Errno = E_BUSY; + SL_SINGLE_THREAD_EXIT(R_FAIL); + } + + /* Is there data currently pending transmission...? + */ + if(spNetCon->spXmitBuf == NULL && szData != NULL) + { + /* Pre-load sizing variables. + */ + spNetCon->nXmitLen = nDataLen; + if(spNetCon->nRawMode == FALSE) spNetCon->nXmitLen += 8; + spNetCon->nXmitPos = 0; + + /* Set the new buffer flag to indicate that we have just undertaken + * to transmit a new buffer. This flag affects the return code in + * the event that we get a busy on the send port. + */ + nNewBuf = TRUE; + + /* If not in Raw Mode, format the data in format: + * <..DATA..> + */ + if((spNetCon->spXmitBuf=(UCHAR *)malloc(spNetCon->nXmitLen)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + spNetCon->nXmitLen); + Errno = E_NOMEM; + SL_SINGLE_THREAD_EXIT(nReturn); + } else + { + /* Copy data as is into buffer. + */ + if(spNetCon->nRawMode == FALSE) + { + memcpy((UCHAR *)spNetCon->spXmitBuf+5,(UCHAR *)szData,nDataLen); + + /* Add formatting data. + */ + spNetCon->spXmitBuf[0] = A_SYN; + spNetCon->spXmitBuf[1] = A_SYN; + spNetCon->spXmitBuf[2] = A_STX; + PutCharFromInt(&spNetCon->spXmitBuf[3], nDataLen); + spNetCon->spXmitBuf[nDataLen+5] = A_ETX; + + /* Finally, get CRC on data. + */ + nDataCRC = _SL_CalcCRC(szData, nDataLen); + PutCharFromInt(&spNetCon->spXmitBuf[nDataLen+6], nDataCRC); + } else + { + memcpy((UCHAR *)spNetCon->spXmitBuf, (UCHAR *)szData, nDataLen); + } + } + } + + /* If we've got info to xmit, then transmit it. + */ + if(spNetCon->spXmitBuf != NULL) + { + switch(spNetCon->nStatus) + { + case SSL_UP: + /* Send the data, if an error occurs, group into relevant + * internal code. + */ + nSend = send(spNetCon->nSd, + &spNetCon->spXmitBuf[spNetCon->nXmitPos], + spNetCon->nXmitLen - spNetCon->nXmitPos, 0); + + /* Any failure's. + */ + if(nSend == -1) + { +#if defined(_WIN32) + /* Windows again folks, old bill just doesnt like standards. + */ + switch((nWinErr = WSAGetLastError())) +#endif +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) + switch(errno) +#endif + { + case EINTR: + case ENOBUFS: + case EWOULDBLOCK: + Errno = E_BUSY; + break; + case EBADF: + case EFAULT: + case EINVAL: + case ENOTSOCK: + default: + Errno = E_BADSOCKET; + break; + } + } else + { + /* Move position pointer on according to number of bytes + * transmitted. + */ + spNetCon->nXmitPos += nSend; + + /* If we've finished transmitting the entire buffer, then + * clean up. + */ + if(spNetCon->nXmitPos == spNetCon->nXmitLen) + { + /* No further need for dynamic buffer. + */ + free(spNetCon->spXmitBuf); + spNetCon->spXmitBuf = NULL; + } + nReturn = R_OK; + } + break; + case SSL_LISTENING: + case SSL_DOWN: + Errno = E_NOSERVICE; + break; + case SSL_FAIL: + default: + Errno = E_BADSOCKET; + break; + } + } else + { + nReturn = R_OK; + } + + /* If a failure occurs due to the send-buffer becoming full, tell + * the user that the packet has been sent ok, as we'll flush it out + * in the background. + */ + if(nNewBuf == TRUE && nReturn == R_FAIL && Errno == E_BUSY) + { + nReturn = R_OK; + } + + /* Return result code to caller. + */ + SL_SINGLE_THREAD_EXIT(nReturn); +} + +/****************************************************************************** + * Function: SL_BlockSendData + * Description: Transmit a packet of data to a given destination but ensure it + * is sent prior to exit. If an error occurs, then return it + * to the caller. + * Thread Safe: No, API function, only allows one thread at a time. + * Returns: R_OK - Data sent successfully. + * R_FAIL - Couldnt send data, see Errno. + * E_INVCHANID - Invalid channel Id. + * E_BADSOCKET - Internal failure on socket, terminal. + * E_NOSERVICE - No remote connection established yet. + ******************************************************************************/ +int SL_BlockSendData( UINT nChanId, /* I: Channel Id to send data on */ + UCHAR *szData, /* I: Data to be sent */ + UINT nDataLen ) /* I: Length of data */ +{ + /* Local variables. + */ + int nReturn; + + SL_SINGLE_THREAD_ONLY; + + /* Flush out any data that is already queued for transmission. + */ + for(; (nReturn=SL_SendData(nChanId, NULL, 0))==R_FAIL && Errno == E_BUSY; ); + + /* Check to ensure that no other failure occurred with flushing the + * buffers. If there is, pass back values setup by SL_SendData. + */ + if(nReturn != R_OK) + { + SL_SINGLE_THREAD_EXIT(nReturn); + } + + /* Send the actual data. Return if an error occurs. + */ + if((nReturn=SL_SendData(nChanId, szData, nDataLen)) == R_FAIL) + { + SL_SINGLE_THREAD_EXIT(nReturn); + } + + /* Flush out the data which we have just queued. + */ + for(; (nReturn=SL_SendData(nChanId, NULL, 0))==R_FAIL && Errno == E_BUSY; ); + + /* Return the result code given when flushing the data out as it + * will reflect any errors exactly. + */ + SL_SINGLE_THREAD_EXIT(nReturn); +} + +/****************************************************************************** + * Function: SL_Poll + * Description: Function for programs which cant afford UX taking control of + * the CPU. This function offers these type of applications the + * ability to allow comms processing by frequently calling this + * Poll function. + * Thread Safe: No, API function, only allows one thread at a time. + * Returns: R_OK - System closing down. + * R_FAIL - Catastrophe, see Errno. + * + ******************************************************************************/ +int SL_Poll( ULNG lSleepTime ) +{ + /* Local variables. + */ + int nReturn = R_OK; + + SL_SINGLE_THREAD_ONLY; + + /* Process list of callback routines. A callback works in time, when + * a certain amount of time has elapsed an application provided + * function is called. + */ + _SL_ProcessCallbacks(); + + /* Any sockets awaiting attention? + */ + _SL_ProcessWaitingPorts(lSleepTime); + + /* Return result code to caller. + */ + SL_SINGLE_THREAD_EXIT(nReturn); +} + +/****************************************************************************** + * Function: SL_Kernel + * Description: Application process control is passed over to this function + * and it allocates and manages time/events. The application + * registers callbacks with this library, and they are invoked + * as events occur or as time elapses. Control passes out of + * this function on application completion. + * Thread Safe: No, Assumes main thread or one control thread. + * Returns: R_OK - System closing down. + * R_FAIL - Catastrophe, see Errno. + * + ******************************************************************************/ +int SL_Kernel( void ) +{ + /* Local variables. + */ + int nReturn = R_FAIL; + ULNG nHibernationPeriod; + + /* Stay in this function forever until a serious event occurs or an + * external event modifies the CloseDown flag. + */ + Sl.nCloseDown = 0; + + do { + /* Process list of callback routines. A callback works in time, when + * a certain amount of time has elapsed an application provided + * function is called. + */ + nHibernationPeriod=_SL_ProcessCallbacks(); + + /* Any sockets awaiting attention? + */ + _SL_ProcessWaitingPorts(1000); + } while(!Sl.nCloseDown); + + /* Return result code to caller. + */ + return(nReturn); +} diff --git a/ux/ux_comms.h b/ux/ux_comms.h new file mode 100755 index 0000000..1a21377 --- /dev/null +++ b/ux/ux_comms.h @@ -0,0 +1,236 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_comms.h + * Description: Generic communications routines. + * + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef UX_COMMS_H +#define UX_COMMS_H + +#define SL_SINGLE_THREAD_ONLY +#define SL_THREAD_ONLY + + + +/* Windows comms result values are different to unix, so define them + * here. +*/ +#if defined(_WIN32) +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINVAL WSAEINVAL +#define EINPROGRESS WSAEINPROGRESS +#define EALREADY WSAEALREADY +#define ENOTSOCK WSAENOTSOCK +#define EDESTADDRREQ WSAEDESTADDRREQ +#define EMSGSIZE WSAEMSGSIZE +#define EPROTOTYPE WSAEPROTOTYPE +#define ENOPROTOOPT WSAENOPROTOOPT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#define EOPNOTSUPP WSAEOPNOTSUPP +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EADDRINUSE WSAEADDRINUSE +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define ENETDOWN WSAENETDOWN +#define ENETUNREACH WSAENETUNREACH +#define ENETRESET WSAENETRESET +#define ECONNABORTED WSAECONNABORTED +#define ECONNRESET WSAECONNRESET +#define ENOBUFS WSAENOBUFS +#define EISCONN WSAEISCONN +#define ENOTCONN WSAENOTCONN +#define ESHUTDOWN WSAESHUTDOWN +#define ETOOMANYREFS WSAETOOMANYREFS +#define ETIMEDOUT WSAETIMEDOUT +#define ECONNREFUSED WSAECONNREFUSED +#define ELOOP WSAELOOP +#define EHOSTDOWN WSAEHOSTDOWN +#define EHOSTUNREACH WSAEHOSTUNREACH +#define EPROCLIM WSAEPROCLIM +#define EUSERS WSAEUSERS +#define EDQUOT WSAEDQUOT +#define ESTALE WSAESTALE +#define EREMOTE WSAEREMOTE + +/* Getdtablesize is not implemented in windows, so we set a fixed value + * for the RLIMIT_NOFILE equivalent. +*/ +#define MAX_WIN_RLIMIT 256 +#endif + +/* Defaults +*/ +#define DEF_CHANID 1000 /* Starting internal comms chan Id */ +#define DEF_BUFINCSIZE 65536 /* Default comms buffer increment */ +#define DEF_INITRECVBUF 524288 /* Default size of comms receive buffer */ +#define DEF_MAXBLOCKPERIOD 10000 /* Default max select sleep period in mS */ +#define DEF_CONWAITPER 50 /* Default wait period for reconnect */ +#define DEF_CONFAILPER 30000 /* Default wait period for a fail */ + +/* Communications framing characters. +*/ +#define A_STX 0x02 /* Start of Text */ +#define A_ETX 0x03 /* End of Text */ +#define A_SYN 0x22 /* Synchronise */ + +/* Timer callback option flags. +*/ +#define TCB_OFF 0 /* Disable callback */ +#define TCB_FLIPFLOP 1 /* Callback toggles, not uniform in time */ +#define TCB_ASTABLE 2 /* Callback toggles, uniform in time */ +#define TCB_ONESHOT 3 /* Callback occurs only once */ + +/* Timer callback status flags. +*/ +#define TCB_UP 128 /* Callback is active */ +#define TCB_DOWN 129 /* Callback is in-active */ + +/* Socket/Line status flags. +*/ +#define SSL_UP 128 /* Socket/Line is up */ +#define SSL_DOWN 129 /* Socket/Line is down */ +#define SSL_LISTENING 130 /* Socket is listening for connections */ +#define SSL_FAIL 131 /* Socket/Line failure */ + +/* Connection type flags. +*/ +#define STP_SERVER 'S' /* Connection is a server */ +#define STP_CLIENT 'C' /* Connection is a client */ + +/* Communication callback control types. These are typically used when a + * socket, acting as a server port, receives a connection, or a client + * eventually connects to a server. +*/ +#define SLC_NEWSERVICE 1 /* New service connection event */ +#define SLC_CONNECT 2 /* New client connect event */ +#define SLC_LINKDOWN 3 /* A Client link has gone down */ +#define SLC_LINKFAIL 4 /* A link has failed and been removed */ + +/* Operating system specifics. +*/ +#define SocketClose close + +/* Structure to hold 16 bit CRC lookup values. For speed, the CRC algorithm + * has been constructed using lookup tables. +*/ +typedef struct { + UCHAR cHiCRC; /* Hi Byte of CRC */ + UCHAR cLoCRC; /* Lo Byte of CRC */ +} SL_CRCTAB; + +/* A structure to define and hold a timed callback event. A timed callback + * event is the invocation of a function after a certain period of time. The + * invocation can be single, multiple etc. +*/ +typedef struct { + void (*nCallback)(); /* Function to call when timer expired */ + UINT nOptions; /* Options controlling callback */ + UINT nStatus; /* Status. Active or Inactive */ + ULNG lTimeExpire; /* Time when callback is triggered */ + ULNG lTimePeriod; /* Period inbetween triggers */ + ULNG lCBData; /* Callback specific data */ +} SL_CALLIST; + +/* A structure to define and maintain a connection, either server of client + * with its opposite on another process. +*/ +typedef struct { + UINT nChanId; /* Internal channel Id associated with link */ + UINT nClose; /* Flag for sync socket closure */ + UINT nForkForAccept; /* Fork a child prior to every accept on srv port */ + UINT nOurPortNo; /* Port number where using */ + UINT nRawMode; /* No prepackaging and post packaging of data */ + UINT nRecvLen; /* Current number of bytes in receive buffer */ + UINT nRecvBufLen; /* Current size of receive buffer */ + UINT nServerPortNo; /* Port number of server service */ + UINT nStatus; /* Status of link */ + UINT nXmitLen; /* Total length of data in xmit buffer */ + UINT nXmitPos; /* Pos in buffer for xmission */ + int nSd; /* Socket descriptor */ + ULNG lDownTimer; /* Amount of time a downed connection remains idle*/ + ULNG lServerIPaddr; /* IP address of server */ + UCHAR cCorS; /* (C) or (S)erver */ + UCHAR *spRecvBuf; /* Flat, dynamic expand/shrink receive buffer */ + UCHAR *spXmitBuf; /* Singular xmit buffer, to contain 1 packet */ + UCHAR szServerName[MAX_SERVERNAME+1];/* Name of server */ + void (*nDataCallback)(); /* Function to call with data */ + void (*nCntrlCallback)(int, ...); /* Function to call with out-of-band info */ +} SL_NETCONS; + +/* Global variables for the Comms module. +*/ +typedef struct { + LINKLIST *spHead; /* Head of LinkedList containing connections */ + LINKLIST *spTail; /* Tail ... */ + LINKLIST *spCBHead; /* Head of LinkedList containing timer callbacks */ + LINKLIST *spCBTail; /* Tail ... */ + UINT nCloseDown; /* Shutdown in progress flag */ + UINT nSockKeepAlive; /* Time to keep socket alive */ +} SL_GLOBALS; + +/* Prototypes for functions internal to SocketLib module. +*/ +UINT _SL_CalcCRC( UCHAR *, UINT ); +UINT _SL_CheckCRC( UCHAR *, UINT ); +int _SL_FdBlocking( int, int ); +UINT _SL_GetPortNo( SL_NETCONS * ); +int _SL_AcceptClient( UINT, SL_NETCONS *, SL_NETCONS ** ); +int _SL_Close( SL_NETCONS *, UINT ); +int _SL_ConnectToServer( SL_NETCONS * ); +int _SL_ReceiveFromSocket( SL_NETCONS * ); +int _SL_ProcessRecvBuf( SL_NETCONS * ); +int _SL_ProcessWaitingPorts( ULNG ); +ULNG _SL_ProcessCallbacks( void ); + +/* Prototypes to externally visible and usable functions. +*/ +UCHAR *SL_HostIPtoString( ULNG ); +int SL_GetIPaddr( UCHAR *, ULNG * ); +int SL_GetService( UCHAR *, UINT * ); +int SL_Init( UINT, UCHAR * ); +int SL_Exit( UCHAR * ); +void SL_PostTerminate( void ); +UINT SL_GetChanId( ULNG ); +int SL_RawMode( UINT, UINT ); +int SL_AddServer( UINT, UINT, void (*)(), void (*)(int, ...) ); +int SL_AddClient( UINT, ULNG, UCHAR *, void (*)(), void (*)(int, ...) ); +int SL_AddTimerCB( ULNG, UINT, ULNG, void (*)() ); +int SL_DelServer( UINT ); +int SL_DelClient( UINT ); +int SL_Close( UINT ); +int SL_SendData( UINT, UCHAR *, UINT ); +int SL_BlockSendData( UINT, UCHAR *, UINT ); +int SL_Poll( ULNG ); +int SL_Kernel( void ); + +#endif /* UX_COMMS_H */ diff --git a/ux/ux_comon.h b/ux/ux_comon.h new file mode 100755 index 0000000..ddb2adf --- /dev/null +++ b/ux/ux_comon.h @@ -0,0 +1,114 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_comon.h + * Description: General purpose library routines. + * + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef UX_COMMON_H +#define UX_COMMON_H + +/* Definitions for Linked List sorting. +*/ +#define SORT_NONE 0 /* No linklist sorting */ +#define SORT_INT_UP 1 /* Linklist sorted incrementally on int */ +#define SORT_INT_DOWN 2 /* Linklist sorted decrementally on int */ +#define SORT_LONG_UP 3 /* Linklist sorted incrementally on long */ +#define SORT_LONG_DOWN 4 /* Linklist sorted decrementally on long */ +#define SORT_CHAR_UP 5 /* Linklist sorted on alpha string */ +#define SORT_CHAR_DOWN 6 /* Linklist sorted in reverseo on alpha string*/ + +/* Logger definitions. Define's mode and level logger operates at. +*/ +#define LOG_OFF 0 /* LEVEL: Logging off */ +#define LOG_CONFIG 1 /* LEVEL: Logger being configured */ +#define LOG_DEBUG 2 /* LEVEL: All debug messages and above */ +#define LOG_WARNING 3 /* LEVEL: All warning messages and above */ +#define LOG_MESSAGE 4 /* LEVEL: All information messages and above */ +#define LOG_ALERT 5 /* LEVEL: All alert messages and above */ +#define LOG_FATAL 6 /* LEVEL: All fatal messages */ +#define LOG_DIRECT 7 /* LEVEL: Always log to stdout if logging on */ +#define LGM_OFF 0 /* MODE: Logger switched off */ +#define LGM_STDOUT 1 /* MODE: Logger logging to stdout */ +#define LGM_FLATFILE 2 /* MODE: Logger logging to flat file */ +#define LGM_DB 3 /* MODE: Logger logging to database */ +#define LGM_ALL 4 /* MODE: Logger logging to all destinations */ + +/* Parser token flags. Identifies the type of token encountered in a parsing + * input stream. +*/ +#define TOK_EOB 0 /* End of Buffer */ +#define TOK_ALPHA 1 /* Token is an alphabetic word */ +#define TOK_ALPHANUM 2 /* Token is an alphanumeric word */ +#define TOK_STRING 3 /* Token is a complete string */ +#define TOK_NUMERIC 4 /* Token is a numeric */ +#define TOK_COMMENT 5 /* Token is a comment */ +#define TOK_CHAR 6 /* Token is a character */ + +/* Define prototypes for functions globally available. +*/ +int AddItem(LINKLIST **, LINKLIST **, int, UINT *, ULNG *, UCHAR *, void *); +int DelItem( LINKLIST **, LINKLIST **, void *, UINT *, ULNG *, UCHAR * ); +void *FindItem( LINKLIST *, UINT *, ULNG *, UCHAR * ); +void *StartItem( LINKLIST *, LINKLIST ** ); +void *NextItem( LINKLIST ** ); +int MergeLists( LINKLIST **, LINKLIST **, LINKLIST *, LINKLIST *, int ); +int DelList( LINKLIST **, LINKLIST ** ); +int SizeList( LINKLIST *, UINT * ); +int PutCharFromLong( UCHAR *, ULNG ); +int PutCharFromInt( UCHAR *, UINT ); +ULNG GetLongFromChar( UCHAR * ); +UINT GetIntFromChar( UCHAR * ); +UINT StrPut( UCHAR *, UCHAR *, UINT ); +void FFwdOverWhiteSpace( UCHAR *, UINT * ); +UINT ParseForToken( UCHAR *, UINT *, UCHAR * ); +int ParseForString( UCHAR *, UINT *, UCHAR * ); +int ParseForInteger( UCHAR *, UINT *, UINT *, UINT *, int * ); +int ParseForLong( UCHAR *, UINT *, long *, long *, long * ); +UCHAR *Compress( UCHAR *, UINT * ); +UCHAR *Decompress( UCHAR *, UINT * ); +void Lgr( int, ... ); +int GetCLIParam( int, UCHAR **, UCHAR *, UINT, UCHAR *, UINT, UINT ); +char *StrRTrim( char * ); +int StrCaseCmp( const char *, const char * ); +int StrnCaseCmp(const char *, const char *, size_t ); +int SplitFQFN( char *, char **, char ** ); + +/* For windows, we must logically associate the case insensitive comparator + * functions with those in this library rather than those in the operating + * system as at the current moment (6/96) Windows does not provide them. +*/ +#if defined(_WIN32) +#define strcasecmp StrCaseCmp +#define strncasecmp StrnCaseCmp +#endif + +#endif /* UX_COMMON_H */ diff --git a/ux/ux_dtype.h b/ux/ux_dtype.h new file mode 100755 index 0000000..3150cde --- /dev/null +++ b/ux/ux_dtype.h @@ -0,0 +1,152 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_dtype.h + * Description: Header file for declaration of structures, datatypes etc. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef UX_DATATYPE_H +#define UX_DATATYPE_H + +/* Datatype declarations. Basically need to use unsigned variables in most + * instances, so rather than typing out the full word, we define some + * short names. +*/ +typedef unsigned int UINT; +typedef unsigned long ULNG; +typedef unsigned char UCHAR; +typedef unsigned short USHRT; + +/* Standard declarations. Sometimes they may not be defined. +*/ +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* Define Maxim's, minimums etc. +*/ +#define MAX_DATETIME 27 /* Max len of a date/time buffer */ +#define MAX_DBNAME 32 /* Max len of database name */ +#define MAX_DBROWSIZE 255 /* Max size of a database row */ +#define MAX_DESTMAPENTRIES 255 /* Max number of dest map lookup entries */ +#define MAX_ERRMSG 2048 /* Max size of an error message */ +#define MAX_FILEDESCR 256 /* Max number of Socket/File descriptors */ +#define MAX_FILENAME 80 /* Max size of a filename */ +#define MAX_HOSTNAME 11 /* Max size of a machine host name */ +#define MAX_INGRESSTMTBUF 131072 /* Max size for Ingres Statement Buffer */ +#define MAX_IPADDR 16 /* Max size of an ascii IP address */ +#define MAX_MACHINENAME 40 /* Max size of a machines name */ +#define MAX_PACKETS 256 /* Max number of packets in the inqueue */ +#define MAX_PATHLEN 128 /* Max len of a pathname */ +#define MAX_PROGNAME 80 /* Max size of a program name */ +#define MAX_QDATA 1530 /* Max size of Q data */ +#define MAX_RECVBUFSIZE 1048576 /* Max size a rcv buf can grow to */ +#define MAX_RECVLEN 2048 /* Max size of a single receive */ +#define MAX_SENDBUFFER 2048 /* Max size of a transmit buffer */ +#define MAX_SERVERNAME 32 /* Max size of a server name */ +#define MAX_SERVICENAME 50 /* Max size of a service name */ +#define MAX_SOCKETBACKLOG 5 /* Max number of incoming socket backlog */ +#define MAX_SYB_INQUPD 15 /* Max simultaneous number of InQ updates */ +#define MAX_SYB_OUTQUPD 15 /* Max simultaneous number of OutQ Upd */ +#define MAX_TABLENAME 32 /* Max size of a table name */ +#define MAX_TMPBUFLEN 2048 /* Max size of a temporary buffer */ +#define MAX_USERNAME 32 /* Max size of a user name */ +#define MAX_USERPWD 32 /* Max size of a user password */ +#define MAX_VARARGBUF 8192 /* Max size of a vararg sprintf buffer */ +#define MAX_XLATEBUF 10240 /* Max size of an sql xlate buffer */ +#define MIN_COMPRESSLEN 256 /* Min size of data prior to compression */ + +/* Definitions for function return parameters. +*/ +#undef R_OK /* Linux declares R_OK for Read permissions, so undefine as name clash and not needed. */ +#define R_OK 0 +#define R_FAIL 1 +#define R_EXIT 2 + +/* Definitions for describing a target variable to a called function. Used + * primarily in switches. +*/ +#define T_INT 3000 /* Type is for integer */ +#define T_STR 3001 /* Type is for string */ +#define T_CHAR 3002 /* Type is for character */ +#define T_LONG 3003 /* Type is for long */ + +/* Error return codes from functions within the Replication System. Generally + * returned by a function in the global external 'errno'. +*/ +#define E_BADHEAD 1 /* Bad Head pointer in Link List */ +#define E_BADTAIL 2 /* Bad Tail pointer in Link List */ +#define E_BADPARM 3 /* Bad parameters passed to a function */ +#define E_NOKEY 4 /* No Link List search key */ +#define E_MEMFREE 5 /* Couldnt free allocated memory */ +#define E_NOMEM 6 /* Couldnt allocate memory */ +#define E_EXISTS 7 /* A Link List entry already exists */ +#define E_NOBIND 8 /* Couldnt perform a socket bind */ +#define E_NOLISTEN 9 /* Couldnt perform a socket listen */ +#define E_NOSOCKET 10 /* Couldnt allocate a socket */ +#define E_BADACCEPT 11 /* Couldnt perform an accept on a socket */ +#define E_INVCHANID 12 /* Invalid channel Id passed to comms lib */ +#define E_NOSERVICE 13 /* No service provider for a client */ +#define E_BADCONNECT 14 /* Couldnt issue a successful connect */ +#define E_NOCONNECT 15 /* Couldnt perform a socket connect */ +#define E_BUSY 16 /* Comms channel is busy, retry later */ +#define E_BADSOCKET 17 /* Given socket is bad or not connected */ +#define E_BADSELECT 18 /* Bad parameters causing select to fail */ +#define E_NODBSERVER 19 /* No database server available */ +#define E_NODATA 20 /* No data available */ +#define E_DBNOTINIT 21 /* Database not initialised */ + +/* Own internal link list handling. Simple progressive link list, with the + * header containing the key elements. In this case, one of each type is + * given, thereby allowing searches without knowing the structure of + * the underlying code. +*/ +typedef struct linklist { + UINT nKey; + ULNG lKey; + UCHAR *szKey; + void *spData; + struct linklist *spNext; +} LINKLIST; + +/* Need a reference to the external errorno which the UX library procedures make + * use of. +extern int errno; +*/ +#if defined(UX_COMMS_C) + int Errno; +#else + extern int Errno; +#endif + +#endif /* UX_DATATYPE_H */ diff --git a/ux/ux_lgr.c b/ux/ux_lgr.c new file mode 100755 index 0000000..b5d201c --- /dev/null +++ b/ux/ux_lgr.c @@ -0,0 +1,255 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_lgr.c + * Description: General purpose standalone (programmable) logging + * utilities. + * + * 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 . + ******************************************************************************/ + +/* Bring in system header files. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(SUNOS) || defined(SOLARIS) || defined(LINUX) +#include +#include +#include +#include +#endif + +#if defined(SOLARIS) +#include +#endif + +#if defined(LINUX) +#include +#endif + +#if defined(_WIN32) +#include +#include +#endif + +#if defined(SUNOS) || defined(SOLARIS) +#include +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define UX_LOGGER_C + +/* Bring in specific header files. +*/ +#include "ux.h" + +/****************************************************************************** + * Function: Lgr + * Description: A function to log a message to a flatfile, database or both. + * Returns: Non. + ******************************************************************************/ +void Lgr( int nLevel, /* I: Level of error message/or command */ + ... ) /* I: Varargs */ +{ + /* Static's. + */ +#ifdef SL_MONITOR + static UCHAR nAlert = FALSE; +#endif + static UINT nLogMode = LGM_STDOUT; + static int nErrLevel = LOG_MESSAGE; + static UCHAR *szLogFile = NULL; + + /* Local variables. + */ + va_list pArgs; + struct tm sTime; + time_t nTime; + UCHAR szBuf[MAX_VARARGBUF]; + UCHAR *szFormat; + UCHAR *szFuncName; +#ifdef SL_MONITOR + UCHAR szMonBuf[MAX_ERRMSG]; +#endif + UCHAR szTime[50]; + FILE *fp; + + /* Start variable argument passing. + */ + va_start(pArgs, nLevel); + + /* Is this a configuration call? If so, set up for future calls. + */ + if(nLevel == LOG_CONFIG) + { + /* Extract varargs off stack. Caller should have called with the + * format: (nLevel, nLogMode, nErrLevel, szProgName) + */ + nLogMode = va_arg(pArgs, UINT); + nErrLevel = va_arg(pArgs, UINT); + szLogFile = va_arg(pArgs, UCHAR *); + + /* Tidy up for exit and return to caller. + */ + va_end(pArgs); + return; + } + + /* If the logger is switched off, just exit. + */ + if((nErrLevel == LOG_OFF || nLogMode == LGM_OFF) && nLevel != LOG_DIRECT) + { + /* Tidy up for exit and return to caller. + */ + va_end(pArgs); + return; + } + + /* Is the level of this error worth logging.. ie. When configured, the + * caller sets a level of which future calls have to equal or better. + */ +#if defined(UX_DEBUG) + if(TRUE) +#else + if(nLevel >= nErrLevel) +#endif + { + /* Extract varargs off stack. Caller should have called with the + * format: (nLevel, szFuncName, szFormat, ...) + */ + szFuncName = va_arg(pArgs, UCHAR *); + szFormat = va_arg(pArgs, UCHAR *); + + /* Build up full message for logging. + */ + vsprintf(szBuf, szFormat, pArgs); + + /* Get current time to stamp message with. This is only used for non- + * database log messages. + */ + time(&nTime); +#if defined(SOLARIS) + localtime_r(&nTime, &sTime); +#else + memcpy(&sTime, localtime(&nTime), sizeof(struct tm)); +#endif + sprintf(szTime, "%02d/%02d/%02d %02d:%02d:%02d", sTime.tm_mday, + sTime.tm_mon+1, sTime.tm_year, sTime.tm_hour, sTime.tm_min, + sTime.tm_sec); + + /* Log according to programmed mode. + * LGM_OFF - No logging. + * LGM_STDOUT - Log to stdout. + * LGM_FLATFILE- Log to a flatfile. + * LGM_DB - Log to Database. + * LGM_ALL - Log to all output destinations. + */ + if(nLogMode == LGM_STDOUT || nLevel == LOG_DIRECT) + { + /* Print to stdout as requested. + */ + printf("[%d %s %s] %s\n", nLevel, szTime, szFuncName,szBuf); + fflush(stdout); + } + if((nLogMode == LGM_ALL || nLogMode == LGM_FLATFILE) && + nLevel != LOG_DIRECT) + { + /* Only log to flatfile if a program name exists. + */ + if(szLogFile != NULL) + { + /* Build up the name of file to open. + */ + if((fp=fopen(szLogFile, "a")) != NULL) + { + /* Place string in buffer. + */ + fprintf(fp, "[%d %s %s] %s\n", nLevel, szTime, + szFuncName, szBuf); + + /* Close file when finished, in case a crash occurs, + * sys-op wants to see last message. + */ + fclose(fp); + } + } + } + if((nLogMode == LGM_ALL || nLogMode == LGM_DB) && nLevel != LOG_DIRECT) + { + /* Call the database to log the message. + */ + } + } + +/* If monitor processing is enabled, then dispatch all log messages to + * monitor processes. +*/ +#ifdef SL_MONITOR + /* If we've had a previous alert and we've got an new message, + * then send cancel alert. + */ + if(nAlert == TRUE && nLevel >= LOG_MESSAGE) + { + sprintf(szMonBuf, "%c", MON_MSG_CANALERT); + SL_MonitorBroadcast(szMonBuf, strlen(szMonBuf)); + nAlert = FALSE; + } + + /* Look at error level, Alerts and Fatal messages require dispatch + * of an ALERT message to all monitor sites. + */ + if(nLevel >= LOG_ALERT) + { + sprintf(szMonBuf, "%c", MON_MSG_ALERT); + SL_MonitorBroadcast(szMonBuf, strlen(szMonBuf)); + nAlert = TRUE; + } + + /* Build up error message and transmit seperately. + */ + sprintf(szMonBuf, "%c%s %s", MON_MSG_ERRMSG, szTime, szBuf); + SL_MonitorBroadcast(szMonBuf, strlen(szMonBuf)); +#endif + + /* Stop vararg processing... ie tidy up stack. + */ + va_end(pArgs); + + /* Finished, get out!! + */ + return; +} diff --git a/ux/ux_linkl.c b/ux/ux_linkl.c new file mode 100755 index 0000000..b9ee314 --- /dev/null +++ b/ux/ux_linkl.c @@ -0,0 +1,661 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_linkl.c + * Description: A library of linked list functions for creating, deleting, + * searching (etc..) linked lists. + * + * 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 . + ******************************************************************************/ + +/* Bring in system header files. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(SUNOS) || defined(SOLARIS) || defined(LINUX) +#include +#include +#include +#endif + +#if defined(SOLARIS) +#include +#endif + +#if defined(LINUX) +#include +#endif + +#if defined(_WIN32) +#include +#include +#endif + +#if defined(SUNOS) || defined(SOLARIS) +#include +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define UX_LINKEDLIST_C + +/* Bring in specific header files. +*/ +#include "ux.h" + +/****************************************************************************** + * Function: AddItem + * Description: A simplistic mechanism to compose a linked list. The link + * is only singly linked, and items are only added to the tail + * of the list. + * Returns: R_OK - Item added successfully. + * R_FAIL - Failure in addition, see Errno. + * E_NOMEM - Memory exhaustion. + * E_BADHEAD - Head pointer is bad. + * E_BADTAIL - Tail pointer is bad. + * E_NOKEY - No search key provided. + ******************************************************************************/ +int AddItem( LINKLIST **spHead, /* IO: Pointer to head of list */ + LINKLIST **spTail, /* IO: Pointer to tail of list */ + int nMode, /* I: Mode of addition to link */ + UINT *nKey, /* I: Integer based search key */ + ULNG *lKey, /* I: Long based search key */ + UCHAR *szKey, /* I: String based search key */ + void *spData ) /* I: Address of carried data */ +{ + /* Local variables. + */ + char *szFunc = "AddItem"; + LINKLIST *pTmpLRec; + LINKLIST *pTailRec; + LINKLIST *spCur; + LINKLIST *spPrev; + + /* Quick check, no point adding to list if there is no data. + */ + if(spData == NULL) + { + Errno = E_NODATA; + return(R_FAIL); + } + + /* Allocate enough memory for a linklist control block. This will be + * tagged on to the end of the list... eventually! + */ + if( (pTmpLRec=(LINKLIST *)malloc(sizeof(LINKLIST))) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + sizeof(LINKLIST)); + Errno = E_NOMEM; + return(R_FAIL); + } + memset((UCHAR *)pTmpLRec, '\0', sizeof(LINKLIST)); + + /* If a text based search key provided, need to allocate space in which + * to store it. If allocation succeeds, dup key. + */ + if(szKey != NULL) + { + if( (pTmpLRec->szKey=(UCHAR *)malloc(strlen(szKey)+1)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", + strlen(szKey)+1); + free(pTmpLRec); + Errno = E_NOMEM; + return(R_FAIL); + } else + { + strcpy(pTmpLRec->szKey, szKey); + } + } + + /* Populate linklist. + */ + pTmpLRec->nKey = (nKey == NULL ? 0 : *nKey); + pTmpLRec->lKey = (lKey == NULL ? 0L : *lKey); + pTmpLRec->spData = spData; + + /* Right, we have a record, so where do we add it. + */ + if(*spHead == NULL) + { + /* Both pointers look at new element. + */ + *spHead = pTmpLRec; + *spTail = pTmpLRec; + pTmpLRec->spNext = NULL; + } else + { + /* If were sorting the list as we go along, then we need to scan it and + * find the required location. + */ + if(nMode != SORT_NONE) + { + for(spPrev=NULL, spCur= *spHead; spCur != NULL; + spPrev=spCur, spCur=spCur->spNext) + { + if(nMode == SORT_INT_UP && pTmpLRec->nKey < spCur->nKey) + break; + else + if(nMode == SORT_INT_DOWN && pTmpLRec->nKey > spCur->nKey) + break; + else + if(nMode == SORT_LONG_UP && pTmpLRec->lKey < spCur->lKey) + break; + else + if(nMode == SORT_LONG_DOWN && pTmpLRec->lKey > spCur->lKey) + break; + } + + /* Error condition, should not occur? + */ + if(spPrev == NULL && spCur == NULL) + { + if(pTmpLRec->szKey != NULL) + free(pTmpLRec->szKey); + free(pTmpLRec); + return(R_FAIL); + } else + + /* Insert at very beginning of list? + */ + if(spPrev == NULL && spCur != NULL) + { + pTmpLRec->spNext = *spHead; + *spHead = pTmpLRec; + } else + + /* Insert in the middle of the list? + */ + if(spPrev != NULL && spCur != NULL) + { + pTmpLRec->spNext = spPrev->spNext; + spPrev->spNext = pTmpLRec; + } else + + /* Insert at the end of the list! + */ + { + /* Add to tail of list by making tail point to new item, then + * new item becomes the tail. + */ + pTailRec = *spTail; + pTailRec->spNext = pTmpLRec; + *spTail = pTmpLRec; + pTmpLRec->spNext = NULL; + } + } else + { + /* Add to tail of list by making tail point to new item, then + * new item becomes the tail. + */ + pTailRec = *spTail; + pTailRec->spNext = pTmpLRec; + *spTail = pTmpLRec; + pTmpLRec->spNext = NULL; + } + } + + /* Return success or fail...? + */ + return(R_OK); +} + +/****************************************************************************** + * Function: DelItem + * Description: Delete an element from a given linked list. The underlying + * carried data is not freed, it is assumed that the caller + * will free that, as it was the caller that allocated it. + * Returns: R_OK - Item deleted successfully. + * R_FAIL - Failure in deletion, see Errno. + * E_BADHEAD - Head pointer is bad. + * E_BADTAIL - Tail pointer is bad. + * E_MEMFREE - Couldnt free memory to sys pool. + * E_NOKEY - No search key provided. + ******************************************************************************/ +int DelItem( LINKLIST **spHead, /* IO: Pointer to head of list */ + LINKLIST **spTail, /* IO: Pointer to tail of list */ + void *spKey, /* I: Addr of item, direct update */ + UINT *nKey, /* I: Integer based search key */ + ULNG *lKey, /* I: Long based search key */ + UCHAR *szKey ) /* I: String based search key */ +{ + /* Local variables. + */ + int nResult = R_FAIL; + LINKLIST *spCur; + LINKLIST *spPrev; + + /* Check input values. Is head valid? + */ + if(*spHead == NULL) + { + Errno = E_BADHEAD; + return(nResult); + } + + /* Is tail valid? + */ + if(*spTail == NULL) + { + Errno = E_BADTAIL; + return(nResult); + } + + /* Have search keys been provided. + */ + if(spKey == NULL && nKey == NULL && lKey == NULL && szKey == NULL) + { + Errno = E_NOKEY; + return(nResult); + } + + /* Locate item by scanning the list. This may get updated in years to + * come to be a hash/btree lookup/delete.... dream on!! + */ + for(spPrev=NULL, spCur= *spHead; spCur != NULL; + spPrev=spCur, spCur=spCur->spNext) + { + /* See if we have a match! + */ + if( (spKey != NULL && spCur->spData != spKey) || + (nKey != NULL && *nKey != spCur->nKey) || + (lKey != NULL && *lKey != spCur->lKey) || + (szKey != NULL && spCur->szKey != NULL && + strcmp(szKey, spCur->szKey) != 0)) + continue; + + /* OK, found the one. + */ + break; + } + + /* If records not null, then we have located the required record, remove + * it. + */ + if(spCur != NULL) + { + /* Item at beginning of list? + */ + if(spPrev == NULL) + { + /* Point head at next in list. If next is NULL, then list empty, + * so update Tail. + */ + if((*spHead = spCur->spNext) == NULL) + *spTail = NULL; + } else + { + if((spPrev->spNext = spCur->spNext) == NULL) + *spTail = spPrev; + } + + /* Free memory used by removed element. + */ + if(spCur->szKey != NULL) + free(spCur->szKey); + free(spCur); + nResult = R_OK; + } + + /* Return success or fail...? + */ + return(nResult); +} + +/****************************************************************************** + * Function: FindItem + * Description: Find an element in a given linked list. + * Returns: NOTNULL - Item found, address returned. + * NULL - Item not found, see Errno. + * E_BADHEAD - Head pointer is bad. + * E_BADTAIL - Tail pointer is bad. + * E_NOKEY - No search key provided. + ******************************************************************************/ +void *FindItem( LINKLIST *spHead, /* I: Pointer to head of list */ + UINT *nKey, /* I: Integer based search key */ + ULNG *lKey, /* I: Long based search key */ + UCHAR *szKey ) /* I: String based search key */ +{ + /* Local variables. + */ + UCHAR *spResult = NULL; + LINKLIST *spCur; + + /* Quite simple at the momoko, just loop through the list and see if an + * entry exists. Eventually, (he hopes) this could be enhanced to inc + * btree/hash lookup. + */ + for(spCur=spHead; spCur != NULL; spCur=spCur->spNext) + { + if(nKey != NULL && spCur->nKey == *nKey) + break; + + if(lKey != NULL && spCur->lKey == *lKey) + break; + + if(szKey != NULL && strcmp(szKey, spCur->szKey) == 0) + break; + } + + /* Found a record? + */ + if( spCur != NULL ) + spResult = spCur->spData; + + /* Return success or fail...? + */ + return(spResult); +} + +/****************************************************************************** + * Function: StartItem + * Description: Setup pointers for a complete list scan. Return the top most + * list 'data item' to caller. + * Returns: NOTNULL - Item found, address returned. + * NULL - Item not found, see Errno. + * E_BADHEAD - Head pointer is bad. + ******************************************************************************/ +void *StartItem( LINKLIST *spHead, /* I: Pointer to head of list */ + LINKLIST **spNext ) /* O: Pointer to next item in list */ +{ + /* Local variables. + */ + void *spResult = NULL; + + /* Does the list exist yet? + */ + if( spHead == NULL ) + { + Errno = E_BADHEAD; + } else + { + /* Setup pointer to next in link and return pointer to data to caller. + */ + *spNext = (LINKLIST *)spHead->spNext; + spResult = (UCHAR *)spHead->spData; + } + + /* Return success or fail...? + */ + return(spResult); +} + +/****************************************************************************** + * Function: NextItem + * Description: Move to next item in a given list. Return the current 'data + * item' to caller. + * Returns: NOTNULL - Item found, address returned. + * NULL - Item not found, see Errno. + * E_BADPARM - Bad parameter passed to function. + ******************************************************************************/ +void *NextItem( LINKLIST **spNext ) /* O: Pointer to next item in list */ +{ + /* Local variables. + */ + void *spResult = NULL; + LINKLIST *spLink = *spNext; + + /* Check parameter, maybe at end of list already. + */ + if( *spNext == NULL ) + { + Errno = E_BADPARM; + } else + { + /* Extract pointer to data and move down link. + */ + spResult = (UCHAR *)spLink->spData; + *spNext = (LINKLIST *)spLink->spNext; + } + + /* Return success or fail...? + */ + return(spResult); +} + +/****************************************************************************** + * Function: MergeLists + * Description: Merge two list together. The Source list is merged into the + * target list. Lists are re-sorted if required. + * Returns: R_OK - Item added successfully. + * R_FAIL - Failure in addition, see Errno. + * E_NOMEM - Memory exhaustion. + * E_BADHEAD - Head pointer is bad. + * E_BADTAIL - Tail pointer is bad. + * E_NOKEY - No search key provided. + ******************************************************************************/ +int MergeLists( LINKLIST **spDstHead, /* IO: Pointer to head of dest list */ + LINKLIST **spDstTail, /* IO: Pointer to tail of dest list */ + LINKLIST *spSrcHead, /* I: Pointer to head of src list */ + LINKLIST *spSrcTail, /* I: Pointer to tail of src list */ + int nMode ) /* I: Mode of list merging */ +{ + /* Local variables. + */ + LINKLIST *spNext; + LINKLIST *spSrc; + LINKLIST *pTailRec; + LINKLIST *spCur; + LINKLIST *spPrev; + + /* Go through each record in the source list and add onto the destination + * list. + */ + if((spSrc = spSrcHead) != NULL) + spNext = spSrcHead->spNext; + else + spNext = NULL; + + /* Loop through the entire source list and merge into the destination list. + */ + while(spSrc != NULL) + { + /* Right, we have a record, so where do we add it. + */ + if(*spDstHead == NULL) + { + /* Both pointers look at new element. + */ + *spDstHead = spSrc; + *spDstTail = spSrc; + spSrc->spNext = NULL; + } else + { + /* If were sorting the list as we go along, then we need to scan it + * and find the required location. + */ + if(nMode != SORT_NONE) + { + for(spPrev=NULL, spCur= *spDstHead; spCur != NULL; + spPrev=spCur, spCur=spCur->spNext) + { + if(nMode == SORT_INT_UP && spSrc->nKey < spCur->nKey) + break; + else + if(nMode == SORT_INT_DOWN && spSrc->nKey > spCur->nKey) + break; + else + if(nMode == SORT_LONG_UP && spSrc->lKey < spCur->lKey) + break; + else + if(nMode == SORT_LONG_DOWN && spSrc->lKey > spCur->lKey) + break; + } + + /* Insert at very beginning of list? + */ + if(spPrev == NULL && spCur != NULL) + { + spSrc->spNext = *spDstHead; + *spDstHead = spSrc; + } else + + /* Insert in the middle of the list? + */ + if(spPrev != NULL && spCur != NULL) + { + spSrc->spNext = spPrev->spNext; + spPrev->spNext = spSrc; + } else + + /* Insert at the end of the list! + */ + { + /* Add to tail of list by making tail point to new item, + * then new item becomes the tail. + */ + pTailRec = *spDstTail; + pTailRec->spNext = spSrc; + *spDstTail = spSrc; + spSrc->spNext = NULL; + } + } else + { + /* Add to tail of list by making tail point to new item, then + * new item becomes the tail. + */ + pTailRec = *spDstTail; + pTailRec->spNext = spSrc; + *spDstTail = spSrc; + spSrc->spNext = NULL; + } + } + + /* Move to next element. + */ + if((spSrc = spNext) != NULL) + spNext = spSrc->spNext; + } + + /* Return success or fail...? + */ + return(R_OK); +} + +/****************************************************************************** + * Function: DelList + * Description: Delete an entire list and free memory used by the list and the + * underlying carried data. + * Returns: R_OK - List deleted successfully. + * R_FAIL - Failed to delete list, see Errno. + * E_BADHEAD - Head pointer is bad. + * E_BADTAIL - Tail pointer is bad. + ******************************************************************************/ +int DelList( LINKLIST **spHead, /* IO: Pointer to head of list */ + LINKLIST **spTail ) /* IO: Pointer to tail of list */ +{ + /* Local variables. + */ + LINKLIST *spTmp; + LINKLIST *spNext; + + /* Check input values. Is head valid? + */ + if(*spHead == NULL) + { + Errno = E_BADHEAD; + return(R_FAIL); + } + + /* Is tail valid? + */ + if(*spTail == NULL) + { + Errno = E_BADTAIL; + return(R_FAIL); + } + + /* Quite simple, breeze through list, deleting everything. + */ + for(spTmp= *spHead; spTmp != NULL; spTmp=spNext) + { + /* Free any memory allocated for text search buffer. + */ + if(spTmp->szKey != NULL) + free(spTmp->szKey); + + /* Free any memory allocated for data record. + */ + if(spTmp->spData != NULL) + free(spTmp->spData); + + /* Get next element then release element memory. + */ + spNext=spTmp->spNext; + free(spTmp); + } + + /* Tidy up callers pointers. + */ + *spHead = *spTail = NULL; + + /* Got here, perhaps everything worked...! + */ + return(R_OK); +} + +/****************************************************************************** + * Function: SizeList + * Description: Find the total number of elements in a given list by scanning + * it. + * Returns: R_OK - List size calculated. + * R_FAIL - Failed to calculate list size, see Errno. + * E_BADHEAD - Head pointer is bad. + ******************************************************************************/ +int SizeList( LINKLIST *spHead, /* I: Pointer to head of list */ + UINT *nCnt ) /* O: Count of elements in list */ +{ + /* Local variables. + */ + LINKLIST *spTmp; + + /* Check input values. Is head valid? + */ + if(spHead == NULL) + { + Errno = E_BADHEAD; + return(R_FAIL); + } + + /* Quite simple, breeze through list, counting. + */ + for(*nCnt=0,spTmp=spHead; spTmp != NULL; *nCnt += 1, spTmp=spTmp->spNext); + + /* Got here, perhaps everything worked...! + */ + return(R_OK); +} diff --git a/ux/ux_mon.c b/ux/ux_mon.c new file mode 100755 index 0000000..8ac2413 --- /dev/null +++ b/ux/ux_mon.c @@ -0,0 +1,1097 @@ +/****************************************************************************** + * 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 . + ******************************************************************************/ + +/* Bring in system header files. +*/ +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#include +#include +#include +#endif + +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(SOLARIS) +#include +#endif + +#include +#include + +/* 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, "Not Implemented" + "

Not Implemented

\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" + "

Have a Nice Day!

\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" + "Not Found

%s - Not Found

\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. + * + ******************************************************************************/ +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. + * + ******************************************************************************/ +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. + * + ******************************************************************************/ +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. + * + ******************************************************************************/ +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); +} + + diff --git a/ux/ux_mon.h b/ux/ux_mon.h new file mode 100755 index 0000000..6df4003 --- /dev/null +++ b/ux/ux_mon.h @@ -0,0 +1,122 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_mon.h + * Description: Application internal monitor processing system. Allows + * possibility of real-time access to a running application. + * + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef UX_MONITOR_H +#define UX_MONITOR_H + +/* Interactive monitor service types. +*/ +#define MON_SERVICE_HTML 0x00 /* All comms are based on HTML */ +#define MON_SERVICE_NL 0x01 /* All comms are based on Natural Language. */ +#define MON_SERVICE_EXT 0xff /* External service handled by an application interpreter override */ + +/* Interactive monitor commands. +*/ +#define MON_MSG_NEW 0x1F /* New IM process connecting */ +#define MON_MSG_TERMINATE 0x1E /* Terminating IM process */ +#define MON_MSG_REDISPLAY 0x1D /* Redisplay prompt */ +#define MON_MSG_TEST 0x1C /* Test connectivity */ +#define MON_MSG_ERRMSG 0x1B /* Inbound error message */ +#define MON_MSG_ALERT 0x1A /* Red-Alert condition */ +#define MON_MSG_CANALERT 0x19 /* Cancel Red-Alert condition */ + +/* Basic Interactive monitoring command set. +*/ +#define MC_TERMINATE "TERMINATE" +#define MC_HTMLGET "GET" +#define MC_HTMLHEAD "HEAD" +#define MC_HTMLPOST "POST" +#define MC_HTMLPUT "PUT" +#define MC_HTMLDELETE "DELETE" +#define MC_HTMLCONNECT "CONNECT" +#define MC_HTMLOPTIONS "OPTIONS" +#define MC_HTMLTRACE "TRACE" + +/* A structure containing data relevant to each unique connection made + * by a client with the monitor system. +*/ +typedef struct { + UINT nChanId; /* Channel ID descr for Socket Lib */ + UINT nClientPortNo; /* TCP port client connected on */ + ULNG lClientIPaddr; /* IP address of connecting client */ +} ML_CONLIST; + +/* A structure defining the list of commands that the interactive monitor + * port recognises and the associated callbacks for each command. +*/ +typedef struct { + UCHAR *szCommand; /* Command in ascii text format */ + int (*nCallback)(); /* Function to call when text recognised */ +} ML_COMMANDLIST; + +/* A structure defining an individual service offered by this application. +*/ +typedef struct { + UINT nMonPort; /* TCP port service provided on */ + UINT nServiceType; /* Type of service provided */ + int (*nConnectCB)(); /* CB on Client Connection */ + int (*nDisconCB)(); /* CB on Client Disconnection */ + int (*nInterpretOvr)(); /* Override for Interpretation function */ + UCHAR *szServerName; /* Name of server for HTTP responses */ + LINKLIST *spConHead; /* Head of Connection List */ + LINKLIST *spConTail; /* Tail of Connection List */ + LINKLIST *spMCHead; /* Head of LList containing I/Mon commands */ + LINKLIST *spMCTail; /* Tail of LList containing I/Mon commands */ +} ML_MONLIST; + +/* Global variables for the Comms module. +*/ +typedef struct { + LINKLIST *spMonHead; /* Head of LList containing I/Mon commands */ + LINKLIST *spMonTail; /* Tail of LList containing I/Mon commands */ +} ML_GLOBALS; + +/* Prototypes for functions internal to MonitorLib module. +*/ +int _ML_MonTerminate( UINT, UCHAR *, UINT ); +void _ML_MonitorCB( UINT, UCHAR *, UINT ); +void _ML_MonitorCntrl( int, ... ); + +/* Prototypes to externally visible and usable functions. +*/ +int ML_Init( UINT, UINT, UCHAR *, int (*)(), int (*)(), int (*)()); +int ML_Exit( UCHAR * ); +int ML_Send( UINT, UCHAR *, UINT ); +int ML_AddMonCommand( UINT, UCHAR *, int (*)() ); +int ML_DelMonCommand( UINT, UCHAR * ); +int ML_Broadcast( UCHAR *, UINT ); + +#endif /* UX_MONITOR_H */ diff --git a/ux/ux_str.c b/ux/ux_str.c new file mode 100755 index 0000000..f8e5644 --- /dev/null +++ b/ux/ux_str.c @@ -0,0 +1,766 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_str.c + * Description: General purpose string processing funtions. Additions to + * those which exist within the C libraries. + * + * 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 . + ******************************************************************************/ + +/* Bring in system header files. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(SUNOS) || defined(SOLARIS) || defined(LINUX) +#include +#include +#include +#endif + +#if defined(SOLARIS) +#include +#endif + +#if defined(LINUX) +#include +#endif + +#if defined(_WIN32) +#include +#include +#endif + +#if defined(SUNOS) || defined(SOLARIS) +#include +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define UX_STRINGPROCESSING_C + +/* Bring in specific header files. +*/ +#include "ux.h" + +/****************************************************************************** + * Function: PutCharFromLong + * Description: Place a long type variable into a character buffer in a known + * byte order. IE. A long is 32 bit, and is placed into the + * char buffer as MSB (3), 2, 1, LSB (0). Where MSB fits into the + * first byte of the buffer. + * Returns: R_OK - Cannot fail ... will I be eating my words...? + ******************************************************************************/ +int PutCharFromLong( UCHAR *pDestBuf, /* O: Destination buffer */ + ULNG lVar ) /* I: Variable of type long */ +{ + /* Local variables. + */ + int nReturn = R_OK; + ULNG lVal = lVar; + + /* Cant assume any byte ordering, so use arithmetic to break it down + * into fundamental components. Assume a long is 32bit. + */ + pDestBuf[0] = (UCHAR)(lVal/16777216L); + lVal -= (ULNG)pDestBuf[0] * 16777216L; + pDestBuf[1] = (UCHAR)(lVal/65536L); + lVal -= (ULNG)pDestBuf[1] * 65536L; + pDestBuf[2] = (UCHAR)(lVal/256L); + lVal -= (ULNG)pDestBuf[2] * 256L; + pDestBuf[3] = (UCHAR)lVal; + + /* Finished, get out!! + */ + return( nReturn ); +} + +/****************************************************************************** + * Function: PutCharFromInt + * Description: Place an int type variable into a character buffer in a known + * byte order. IE. An int is 16 bit, and is placed into the + * char buffer as MSB (1), LSB (0). Where MSB fits into the + * first byte of the buffer. + * Returns: R_OK - Cannot fail ... see comment above. + ******************************************************************************/ +int PutCharFromInt( UCHAR *pDestBuf, /* O: Destination buffer */ + UINT lVar ) /* I: Variable of type int */ +{ + /* Local variables. + */ + int nReturn = R_OK; + ULNG lVal = (ULNG)lVar; + + /* Cant assume any byte ordering, so use arithmetic to break it down + * into fundamental components. Assume 16 bit Int. + */ + if(lVal > 65536L) + { + /* > 16 bit, so remove upper component. + */ + lVal -= (lVal/16777216L)*65536L; + } + pDestBuf[0] = (UCHAR)(lVal/256L); + lVal -= (ULNG)pDestBuf[0] * 256L; + pDestBuf[1] = (UCHAR)lVal; + + /* Finished, get out!! + */ + return( nReturn ); +} + +/****************************************************************************** + * Function: GetLongFromChar + * Description: Get a long type variable from a character buffer. The byte + * ordering in the buffer is assumed to be 32bit, MSB(3), 2, 1, + * LSB (0), where the MSB fits into the first byte of the + * buffer. + * Returns: R_OK - Cannot fail ... will I be eating my words...? + ******************************************************************************/ +ULNG GetLongFromChar( UCHAR *pDestBuf ) /* I: Source buffer to convert */ +{ + /* Local variables. + */ + ULNG lVal = 0; + + /* Cant assume any byte ordering, so use arithmetic to break it down + * into fundamental components. Assume a long is 32bit. + */ + lVal = (UCHAR)pDestBuf[0] * 16777216L; + lVal += (UCHAR)pDestBuf[1] * 65536L; + lVal += (UCHAR)pDestBuf[2] * 256L; + lVal += (UCHAR)pDestBuf[3]; + + /* Finished, get out!! + */ + return( lVal ); +} + +/****************************************************************************** + * Function: GetIntFromChar + * Description: Get a long type variable from a character buffer. The byte + * ordering in the buffer is assumed to be 16bit, MSB(1), LSB(0) + * where the MSB fits into the first byte of the buffer. + * Returns: R_OK - Cannot fail ... will I be eating my words...? + ******************************************************************************/ +UINT GetIntFromChar( UCHAR *pDestBuf ) /* I: Source buffer to convert */ +{ + /* Local variables. + */ + UINT lVal = 0; + + /* Cant assume any byte ordering, so use arithmetic to break it down + * into fundamental components. Assume a long is 32bit. + */ + lVal += (UCHAR)pDestBuf[0] * 256; + lVal += (UCHAR)pDestBuf[1]; + + /* Finished, get out!! + */ + return( lVal ); +} + +/****************************************************************************** + * Function: StrPut + * Description: Put a string INTO another string. Same as strcpy BUT it doesnt + * terminate the destination string. + * Returns: R_OK - Cannot fail ... will I be eating my words...? + ******************************************************************************/ +UINT StrPut( UCHAR *spDestBuf, /* I: Destination buffer to copy into */ + UCHAR *spSrcBuf, /* I: Source buffer to copy from */ + UINT nBytes ) /* I: Number of bytes to copy */ +{ + /* Local variables. + */ + UINT nNdx; + + /* Simple copy. + */ + for(nNdx=0; nNdx < nBytes && spSrcBuf[nNdx] != '\0'; nNdx++) + { + spDestBuf[nNdx] = spSrcBuf[nNdx]; + } + + /* Finished, get out!! + */ + return( R_OK ); +} + +/****************************************************************************** + * Function: FFwdOverWhiteSpace + * Description: Forward a pointer past whitespace in the input buffer. + * Returns: Non. + ******************************************************************************/ +void FFwdOverWhiteSpace( UCHAR *szInBuf, /* I: Input data buffer */ + UINT *nPos ) /* IO: Start/End position in buf */ +{ + /* Local variables. + */ + + /* Move the pointer past whitespace to the fisrt non-whitespace character + * or the end of file. + */ + while(szInBuf[*nPos] != '\0' && isspace(szInBuf[*nPos])) + { (*nPos)++; } + + return; +} + +/****************************************************************************** + * Function: ParseForToken + * Description: Parse the input buffer for the next token. A token can be a + * Alpha/Alphanum word, a numeric, a single character or a + * string. + * Returns: Type of Token located. + ******************************************************************************/ +UINT ParseForToken( UCHAR *szInBuf, /* I: Input buffer */ + UINT *nPos, /* IO: Current pos in buffer */ + UCHAR *szTokBuf ) /* O: Token output buffer */ +{ + /* Local variables. + */ + UINT nExit; + UINT nQuoteType; + UINT nToken; + UINT nTokNdx; + + /* Initially, forward to the first non-whitespace. + */ + while(szInBuf[*nPos] != '\0' && isspace(szInBuf[*nPos])) + { + (*nPos)++; + } + + /* End of buffer..? + */ + if(szInBuf[*nPos] == '\0') + { + /* Instruct caller that we are at the end. + */ + nToken = TOK_EOB; + } else + /* Do we have an alpha/alphanum in the buffer? + */ + if(isalpha(szInBuf[*nPos])) + { + /* Instruct caller, initially, that we located an ALPHA word. + */ + nToken = TOK_ALPHA; + + /* Go through the buffer, copying out the word. + */ + for(nTokNdx=0; isalnum(szInBuf[*nPos]) || szInBuf[*nPos] == '-' || + szInBuf[*nPos] == '_' || szInBuf[*nPos] == '/'; + nTokNdx++, (*nPos)++) + { + /* Data copy. + */ + szTokBuf[nTokNdx] = szInBuf[*nPos]; + } + + /* Terminate Token Buffer. + */ + szTokBuf[nTokNdx] = '\0'; + } else + /* Do we have a numeric in the buffer? + */ + if(isdigit(szInBuf[*nPos]) || + (szInBuf[*nPos] == '-' && isdigit(szInBuf[(*nPos)+1])) || + (szInBuf[*nPos] == '+' && isdigit(szInBuf[(*nPos)+1]))) + { + /* Instruct caller that we located a numeric. + */ + nToken = TOK_NUMERIC; + + /* Straight data copy. + */ + for(nTokNdx=0; + isdigit(szInBuf[*nPos]) || szInBuf[*nPos] == '.' || + szInBuf[*nPos] == '-' || szInBuf[*nPos] == '+'; + nTokNdx++, (*nPos)++) + { + szTokBuf[nTokNdx] = szInBuf[*nPos]; + } + + /* Terminate Token Buffer. + */ + szTokBuf[nTokNdx] = '\0'; + } else + /* Do we have a string in the buffer? + */ + if(szInBuf[*nPos] == 0x27 || szInBuf[*nPos] == 0x22) + { + /* Instruct caller that we located a string. + */ + nToken = TOK_STRING; + + /* What type of quote are we looking for? + */ + if(szInBuf[(*nPos)] == 0x27) + nQuoteType = 0; + else + nQuoteType = 1; + + /* Go through the input buffer until we locate the matching quote + * or we hit the end of buffer. + */ + for(nTokNdx=0, nExit=FALSE; nExit == FALSE; nTokNdx++, (*nPos)++) + { + /* Copy over to the token buffer, all chars, including the final + * quote/eob. + */ + szTokBuf[nTokNdx] = szInBuf[*nPos]; + + /* If were looking for single quotes and we locate one which is + * not escaped, then we've reached the end of the string. + */ + if(szInBuf[*nPos] == 0x27 && nQuoteType == 0) + { + /* If where at the beginning of a string and we dont meet + * the criterion for a null string, the loop. + */ + if(nTokNdx == 0 && + szInBuf[(*nPos)+1] != 0x27) + { + continue; + } else + + /* A null string? + */ + if(nTokNdx == 0 && + szInBuf[(*nPos)+1] == 0x27 && + szInBuf[(*nPos)+2] != 0x27) + { + /* Copy final byte into token buffer. + */ + szTokBuf[++nTokNdx] = szInBuf[++(*nPos)]; + nExit = TRUE; + } else + + /* A lone quote? + */ + if(szInBuf[(*nPos)-1] != 0x5c && + szInBuf[(*nPos)+1] != 0x27 && + szInBuf[(*nPos)-1] != 0x27) + { + nExit = TRUE; + } + } else + + /* If were looking for double quotes and we locate on which is + * not escaped, then we've reached the end of the string. + */ + if(szInBuf[*nPos] == 0x22 && nQuoteType == 1) + { + /* If where at the beginning of a string and we dont meet + * the criterion for a null string, then loop. + */ + if(nTokNdx == 0 && + szInBuf[(*nPos)+1] != 0x22) + { + continue; + } else + + /* A null string? + */ + if(szInBuf[(*nPos)+1] == 0x22 && + szInBuf[(*nPos)+2] != 0x22 && + nTokNdx == 0) + { + /* Copy final byte into token buffer. + */ + szTokBuf[++nTokNdx] = szInBuf[++(*nPos)]; + nExit = TRUE; + } else + + /* A lone quote? + */ + if(szInBuf[(*nPos)-1] != 0x5c && + szInBuf[(*nPos)+1] != 0x22 && + szInBuf[(*nPos)-1] != 0x22) + { + nExit = TRUE; + } + } else + + /* Found the end of buffer... some idiot hasnt terminated the + * string correctly. + */ + if(szInBuf[*nPos] == '\0') + nExit = TRUE; + } + + /* Terminate Token Buffer. + */ + szTokBuf[nTokNdx] = '\0'; + } else + /* Is this a comment...? + */ + if(szInBuf[*nPos] == '/' && szInBuf[(*nPos)+1] == '*') + { + /* Instruct caller that we located a comment. + */ + nToken = TOK_COMMENT; + + /* Loop through copying upto the end of the comment. + */ + for(nTokNdx=0, nExit=FALSE; nExit == FALSE; nTokNdx++, (*nPos)++) + { + /* Copy data into token buffer. + */ + szTokBuf[nTokNdx] = szInBuf[*nPos]; + if(nTokNdx < 2) continue; + + /* End of comment? + */ + if(szInBuf[*nPos] == '*' && szInBuf[(*nPos)+1] == '/') + { + nExit = TRUE; + } + + /* Found the end of buffer... some idiot hasnt terminated the + * comment correctly. + */ + if(szInBuf[*nPos] == '\0') + nExit = TRUE; + } + + /* Terminate Token Buffer. + */ + szTokBuf[nTokNdx] = '\0'; + } else + { + /* Instruct caller that we located a character. + */ + nToken = TOK_CHAR; + + /* Straight data copy and termination. + */ + szTokBuf[0] = szInBuf[(*nPos)++]; + szTokBuf[1] = '\0'; + } + + /* Return type to caller. + */ + return(nToken); +} + +/****************************************************************************** + * Function: ParseForString + * Description: Get next valid string from input buffer. + * Returns: Non. + ******************************************************************************/ +int ParseForString( UCHAR *szInBuf, /* I: Input buffer */ + UINT *nPos, /* I: Position in input buffer */ + UCHAR *szOutBuf ) /* O: Target buffer for string */ +{ + /* Local variables. + */ + UINT nReturn = R_OK; + UINT nTokType; + + /* Zero callers buffer. + */ + szOutBuf[0] = '\0'; + + /* Get next parameter. + */ + nTokType = ParseForToken(szInBuf, nPos, szOutBuf); + + /* If its not a valid string, modify return code. + */ + if(nTokType == TOK_COMMENT || nTokType == TOK_CHAR || + nTokType == TOK_EOB || strlen(szOutBuf) == 0) + { + nReturn = R_FAIL; + } + + /* Return result to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: ParseForInteger + * Description: Get next valid integer from input buffer. + * Returns: Non. + ******************************************************************************/ +int ParseForInteger( UCHAR *szInBuf, /* I: Input buffer */ + UINT *nPos, /* I: Position in input buffer */ + UINT *nMin, /* I: Minimum allowable value */ + UINT *nMax, /* I: Maximum allowable value */ + int *pOutInt ) /* O: Target buffer for integer */ +{ + /* Local variables. + */ + UINT nInt; + UINT nReturn = R_OK; + UINT nTokType; + UCHAR szTmpBuf[MAX_TMPBUFLEN]; + + /* Get next parameter. + */ + nTokType = ParseForToken(szInBuf, nPos, szTmpBuf); + + /* Convert into an integer. + */ + if(nTokType != TOK_NUMERIC || sscanf(szTmpBuf, "%d", &nInt) != 1 || + (nMin != NULL && nInt < *nMin) || (nMax != NULL && nInt > *nMax)) + { + nReturn = R_FAIL; + } else + { + *pOutInt = nInt; + } + + /* Return result to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: ParseForLong + * Description: Get next valid Long from input buffer. + * Returns: Non. + ******************************************************************************/ +int ParseForLong( UCHAR *szInBuf, /* I: Input buffer */ + UINT *nPos, /* I: Position in input buffer */ + long *lMin, /* I: Minimum allowable value */ + long *lMax, /* I: Maximum allowable value */ + long *pOutLong ) /* O: Target buffer for Long */ +{ + /* Local variables. + */ + UINT nReturn = R_OK; + UINT nTokType; + long lLng; + UCHAR szTmpBuf[MAX_TMPBUFLEN]; + + /* Get next parameter. + */ + nTokType = ParseForToken(szInBuf, nPos, szTmpBuf); + + /* Convert into an integer. + */ + if(nTokType != TOK_NUMERIC || sscanf(szTmpBuf, "%ld", &lLng) != 1 || + (lMin != NULL && lLng < *lMin) || (lMax != NULL && lLng > *lMax)) + { + nReturn = R_FAIL; + } else + { + *pOutLong = lLng; + } + + /* Return result to caller. + */ + return(nReturn); +} + +/****************************************************************************** + * Function: StrRTrim + * Description: A function to trim off all trailing spaces for a given string. + * The function works by starting at the end of a null terminated + * string and looking for the first non-space character. It then + * places a null terminator at the new location. + * Returns: Pointer to new string. + ******************************************************************************/ +char *StrRTrim( char *szSrc ) /* IO: Base string to trim */ +{ + /* Local variables. + */ + char *pLocation = &szSrc[strlen(szSrc)-1]; + + /* Scan backwards until we find a non space or we meet the beginning + * of the string. + */ + while( pLocation != szSrc && isspace(*pLocation) ) + { + pLocation--; + } + + /* If we are not at the beginning of the string, then place a null + * terminator at t+1; + */ + if(pLocation != szSrc) + { + pLocation++; + *pLocation = '\0'; + } + + /* All done, exit. + */ + return(szSrc); +} + +/****************************************************************************** + * Function: StrCaseCmp + * Description: A function to perform string compares regardless of + * character case. Provided mainly for operating systems that + * dont possess such functionality. + * Returns: 0 - Strings compare. + * > 0 + * < 0 + ******************************************************************************/ +int StrCaseCmp( const char *szSrc, /* I: Base string to compare against */ + const char *szCmp ) /* I: Comparator string */ +{ + /* Local variables. + */ + register signed char cResult; + + /* Loop, progressing through the strings until we have a difference or + * we get to the end of the string and hence have a comparison match. + */ + while(1) + { + cResult = toupper(*szSrc) - toupper(*szCmp); + szCmp++; + szSrc++; + if(cResult != 0 || *szCmp == '\0' || *szSrc == '\0') + break; + } + + /* Return result to caller. + */ + return(cResult); +} + +/****************************************************************************** + * Function: StrnCaseCmp + * Description: A function to perform string compares regardless of + * character case for a specified number of characters within + * both strings. Provided mainly for operating systems that + * dont possess such functionality. + * Returns: 0 - Strings compare. + * > 0 + * < 0 + ******************************************************************************/ +int StrnCaseCmp( const char *szSrc, /* I: Base string to compare against */ + const char *szCmp, /* I: Comparator string */ + size_t nCount ) /* I: Number of bytes to compare */ +{ + /* Local variables. + */ + register signed char cResult; + + /* Loop, progressing through the strings until we have a difference or + * the given number of characters match in both strings. + */ + while(nCount) + { + cResult = toupper(*szSrc) - toupper(*szCmp); + szCmp++; + szSrc++; + if(cResult != 0 || *szCmp == '\0' || *szSrc == '\0') + break; + nCount--; + } + + /* Return result to caller. + */ + return(cResult); +} + +/****************************************************************************** + * Function: SplitFQFN + * Description: A function to split a fully qualified filename into a + * directory and filename components. + * Returns: R_OK - Filename split. + * R_FAIL - Couldnt split due to errors, ie. memory. + ******************************************************************************/ +int SplitFQFN( char *szFQFN, /* I: Fully Qualified File Name */ + char **szDir, /* O: Directory component */ + char **szFN ) /* O: Filename component */ +{ + /* Local variables. + */ + UINT nNdx; + char *spTmpDir; + char *spTmpFN; + char *szFunc = "SplitFQFN"; + + /* Allocate a buffer to hold the directory component. + */ + if((spTmpDir=(UCHAR *)malloc(strlen(szFQFN)+1)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", strlen(szFQFN)+1); + Errno = E_NOMEM; + return(R_FAIL); + } + + /* Allocate a buffer to hold the filename component. + */ + if((spTmpFN=(UCHAR *)malloc(strlen(szFQFN)+1)) == NULL) + { + Lgr(LOG_DEBUG, szFunc, "Couldnt malloc (%d) bytes", strlen(szFQFN)+1); + Errno = E_NOMEM; + return(R_FAIL); + } + + /* Starting at the end of the string, work backwards until we find + * a directory element character or start of string. + */ + for(nNdx=strlen(szFQFN); nNdx >= 0 && szFQFN[nNdx] != '/'; nNdx--); + + /* If nNdx == 0 && szFQFN[nNdx] not equal to the directory character + * then we have a filename with no directory. Just copy. + */ + if(nNdx == 0 && szFQFN[nNdx] != '/') + { + strcpy(spTmpDir, ""); + strcpy(spTmpFN, szFQFN); + } else + if(nNdx == 0 && szFQFN[nNdx] == '/') + { + strcpy(spTmpDir, "/"); + strcpy(spTmpFN, &szFQFN[1]); + } else + { + szFQFN[nNdx] = '\0'; + strcpy(spTmpDir, szFQFN); + strcpy(spTmpFN, &szFQFN[nNdx+1]); + szFQFN[nNdx] = '/'; + } + + /* Setup callers pointers to point to new memory. + */ + *szDir = spTmpDir; + *szFN = spTmpFN; + + /* Return result to caller. + */ + return(R_OK); +} diff --git a/ux/ux_thrd.c b/ux/ux_thrd.c new file mode 100755 index 0000000..4676a2f --- /dev/null +++ b/ux/ux_thrd.c @@ -0,0 +1,223 @@ +/****************************************************************************** + * Product: # # # # # ### ###### + * # # # # # # # # + * # # # # # # # # + * # # # # # ###### + * # # # # # # # # + * # # # # # # # # + * ##### # # ####### ####### ### ###### + * + * File: ux_thrd.c + * Description: UX Thread Management routines. A module with the specific aim + * of invoking, charting and termination of threads required by + * programs built with this library. + * + * 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 . + ******************************************************************************/ + +/* Bring in system header files. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(SUNOS) || defined(SOLARIS) || defined(LINUX) +#include +#include +#include +#endif + +#if defined(SOLARIS) +#include +#endif + +#if defined(LINUX) +#include +#endif + +#if defined(_WIN32) +#include +#include +#endif + +#if defined(SUNOS) || defined(SOLARIS) +#include +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define UX_THREADS_C + +/* Bring in specific header files. +*/ +#include "ux.h" + +/****************************************************************************** + * Internal Library functions. + ******************************************************************************/ + +/* Internal structures: A global Mutex lock set, split up into sections, + * which the threads examine and wait on according to + * their state. + * + * No lock in all _SL functions, can only be entered by the SL library + * thread. During initialisation, store id of SL thread, compare upon entry + * to the library and exit with error if id incorrect. + * + * Single lock for all SL functions. Upon entry, thread checks lock, + * if its free, it sets it and proceeds. If its locked, it waits till it + * becomes free. + * + * +*/ + +/* Internal Function: Create Thread and Add to Linked List. Linked List to + * contain Mutex Locks for local locks and global lock set. + * + * Delete Thread. +*/ + +/****************************************************************************** + * Function: _TM_ + * Description: + * + * Returns: . + ******************************************************************************/ + + + + +/****************************************************************************** + * User API functions. + ******************************************************************************/ + +/* API Function: Create New Thread. Calls Internal function with Mutex + * protection. + * + * API Function: Delete or Terminate existing thread. Calls Internal function. + * + * +*/ + +/****************************************************************************** + * Function: TM_ + * Description: + * + * Returns: . + ******************************************************************************/ + +/****************************************************************************** + * Function: TM_Init + * Description: Function to initialise the Thread Management Library. + * + * Returns: R_OK - Succeeded. + * R_FAIL - Failed to init, see errno and szErrMsg. + ******************************************************************************/ +int TM_Init( UCHAR *szErrMsg ) /* O: Error message if function fails */ +{ + /* Local variables. + */ + char *szFunc = "TM_Init"; + + /* Initialise control&tracking linked list. + */ + + /* Got this far, exit with success. + */ + return(R_OK); +} + +/****************************************************************************** + * Function: TM_Exit + * Description: Function to terminate all threads and clear the + * control&tracking records. Must be the main parent thread + * ie. Thread0 that calls this function otherwise an error + * is returned. + * + * Returns: R_OK - Succeeded. + * R_FAIL - Failed to exit, see errno and szErrMsg. + ******************************************************************************/ +int TM_Exit( UCHAR *szErrMsg ) /* O: Error message if function fails */ +{ + /* Local variables. + */ + char *szFunc = "TM_Init"; + + /* Free up all resources used by the control&tracking linked list. + */ + + /* Got this far, exit with success. + */ + return(R_OK); +} + +/****************************************************************************** + * Function: TM_CreateThread + * Description: Function to create a new thread. Uses current thread as + * base, allocates a new control&tracking record which gets + * stored in the Thread Management linked list and releases + * the thread onto the supplied start function. + * + * Author: P.D. Smart + * Returns: R_OK - Succeeded. + * R_FAIL - Failed to create thread, see errno and szErrMsg. + ******************************************************************************/ +int TM_CreateThread( /* I: Start Function */ + UCHAR *szErrMsg ) /* O: Error msg if func fails */ +{ + /* Local variables. + */ + char *szFunc = "TM_CreateThread"; + + + /* Got this far, exit with success. + */ + return(R_OK); +} + +/****************************************************************************** + * Function: TM_KillThread + * Description: Function to kill a requested thread. Once killed, the + * control&tracking lists are updated. + * + * Returns: R_OK - Succeeded. + * R_FAIL - Failed to kill thread, see errno and szErrMsg. + ******************************************************************************/ +int TM_Create( /* I: Thread Id */ + UCHAR *szErrMsg ) /* O: Error msg if func fails */ +{ + /* Local variables. + */ + char *szFunc = "TM_KillThread"; + + + /* Got this far, exit with success. + */ + return(R_OK); +} + diff --git a/ux_test/.pure b/ux_test/.pure new file mode 100755 index 0000000..e69de29 diff --git a/ux_test/Makefile b/ux_test/Makefile new file mode 100755 index 0000000..e19a9c8 --- /dev/null +++ b/ux_test/Makefile @@ -0,0 +1,152 @@ +#****************************************************************************** +#* Product: +#* ####### ####### ##### ####### ##### # # ### ####### ####### +#* # # # # # # # # # # # # +#* # # # # # # # # # # +#* # ##### ##### # ##### # # # # ##### +#* # # # # # # # # # # +#* # # # # # # # # # # # # +#* # ####### ##### # ##### ##### ##### ### # ####### +#* +#* File: Makefile +#* Description: Build description file for the test suite of programs. +#* 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 . +#****************************************************************************** +TITLE = "Program Test Suite" +COPYRIGHT = "(C) P.D.Smart, %D%, Vers %I%" +PROJ = +PURIFY = #purify +PROJPATH = ../ux_test +GNUINCLUDE = #-I/apps/gnu/$(ARCH)/include +MDCINCLUDE = -I../MDC +SDDINCLUDE = -I../SDD +UXINCLUDE = -I../ux +INCLUDEDIR = -I. $(UXINCLUDE) $(MDCINCLUDE) $(SDDINCLUDE) $(GNUINCLUDE) +1DEBUGFLAGS = -g #-DMDC_DEBUG #-E +4DEBUGFLAGS = -g #-DMDC_DEBUG #-E +5DEBUGFLAGS = -g #-DMDC_DEBUG #-E +1OPTIMIZEFLAGS = #-O2 +4OPTIMIZEFLAGS = #-O2 +5OPTIMIZEFLAGS = #-O2 +1OPTIONFLAGS = -D${OS} #-ansi -Wall +4OPTIONFLAGS = -D${OS} #-ansi -Wall +5OPTIONFLAGS = -D${OS} -D_REENTRANT #-ansi -Wall +CFLAGS = $(${OSVER}DEBUGFLAGS) $(${OSVER}OPTIMIZEFLAGS) \ + $(${OSVER}OPTIONFLAGS) +LDFLAGS = #-static +MDCLIBS = -L../MDC/${OSVER}lib -lmdc +ODBCLIBS = -L../odbc/dlls -lodbc +SDDLIBS = -L../SDD/${OSVER}lib -lsdd +UXLIBS = -L../ux/${OSVER}lib -lux +1SYBLIBS = +4SYBLIBS = -L/apps/sybase/lib -lsybdb +5SYBLIBS = -L/apps/sybase/lib -lsybdb +1LIBS = -lm +4LIBS = -lm +5LIBS = -L/usr/ucblib -lsocket -lnsl -lucb #-liberty -lucb +LIBS = $(UXLIBS) $(${OSVER}LIBS) +SCCSFLAGS = -d$(PROJPATH) +SCCSGETFLAGS = + +ifeq ($(ZPU_BUILD),) +BASE = +else +BASE = zpu-elf- +endif + +CC = $(BASE)gcc +LD = $(BASE)gcc +AS = $(BASE)as +AR = $(BASE)ar +CP = $(BASE)objcopy +DUMP = $(BASE)objdump +RANLIB = $(BASE)ranlib + +# Suffixes where interested in for this project. +# +.SUFFIXES: +.SUFFIXES: .o .c .h + +# Our way of making an object file. +# +.c.o: + $(PURIFY) $(CC) $(INCLUDEDIR) $(CFLAGS) -c $< + +# All, ie: all programs to be built +# +all: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Use 'build' command to make Test Suite." + +TestSuite: Begin \ + test_mon \ + End + +# How to clean up the directory... make it look pretty! +# +clean: Begin \ + DoClean \ + End + +# How to perform an installation of the resultant software. +# +install: Begin \ + DoInstall \ + End + +# +# Pre-make start sequence. +# +Begin: + @echo $(TITLE) + @echo $(COPYRIGHT) + @echo + @echo "Operation commencing @ `date`" + @echo + +# +# Post-make completion sequence. +# +End: + @echo + @echo "Completed @ `date`" + +# Perform all cleanup operations to ensure future builds occur +# with completeness. +# +DoClean: + rm -f *.o *.bak *.a *.BAK *.sav core + +# Perform installation of software as per spec. +# +DoInstall: + +# Build the Monitor Facility test program. +# +test_mon: test_mon.o + $(PURIFY) $(CC) $(LDFLAGS) -o test_mon \ + test_mon.o \ + $(LIBS) + @echo "Monitor Facility Test Program 'test_mon' built." + +test_mon.o: test_mon.c test_mon.h diff --git a/ux_test/build b/ux_test/build new file mode 100755 index 0000000..4e7faff --- /dev/null +++ b/ux_test/build @@ -0,0 +1,170 @@ +#!/bin/csh +#****************************************************************************** +#* Product: +#* ####### ####### ##### ####### ##### # # ### ####### ####### +#* # # # # # # # # # # # # +#* # # # # # # # # # # +#* # ##### ##### # ##### # # # # ##### +#* # # # # # # # # # # +#* # # # # # # # # # # # # +#* # ####### ##### # ##### ##### ##### ### # ####### +#* +#* File: build +#* Description: Build description file for all test programs relevant to the +#* ux libraries. +#* 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 . +#****************************************************************************** + +# Work out architecture we are compiling on.. +# +setenv ARCH `uname -s` +setenv ARCH ${ARCH}`uname -r | cut -c1` + +# Work out arguments and decide on actions from there. +# +if( $#argv > 0 ) then + + switch("$argv[1]") + + case "save": + make -f Makefile clean + sccs delta SCCS + exit 0 + + case "install": + echo "Nothing to install.." + exit 0 + + case "zpu": + setenv ARCH 'ZPU' + set makecmd="TestSuite" + breaksw + + default: + # If the command is not one this script recognises then pass on to + # the makefile. + # + set makecmd="$argv[1]" + endsw +else + set makecmd="TestSuite" +endif + +# Build according to parameter supplied. +# +switch ($ARCH) + case "SunOS4": + setenv OS "SUNOS" + setenv OSVER 4 + echo "SunOS operating system build" + + if( ! -r .sunos ) then + \rm -f .solaris + \rm -f .sunos + \rm -f .linux + \rm -f .zpu + touch .sunos + chmod 777 .sunos + make -f Makefile clean $makecmd + set result = $status + else + make -f Makefile $makecmd + set result = $status + endif + if( $result == 0 && -r test_mon ) then + \mv -f test_mon ${OSVER}bin + endif + breaksw + + case "SunOS5": + setenv OS "SOLARIS" + setenv OSVER 5 + echo "Solaris operating system build" + + if( ! -r .solaris ) then + \rm -f .solaris + \rm -f .sunos + \rm -f .linux + \rm -f .zpu + touch .solaris + chmod 777 .solaris + make -f Makefile clean $makecmd + set result = $status + else + make -f Makefile $makecmd + set result = $status + endif + if( $result == 0 && -r test_mon ) then + \mv -f test_mon ${OSVER}bin + endif + breaksw + + case "Linux2": + case "Linux4": + setenv OS "LINUX" + setenv OSVER 1 + echo "Linux operating system build" + echo "" + if( ! -r .linux ) then + \rm -f .sunos + \rm -f .solaris + \rm -f .linux + \rm -f .zpu + touch .linux + chmod 777 .linux + make -f Makefile clean $makecmd + set result = $status + else + make -f Makefile $makecmd + set result = $status + endif + if( $result == 0 && -r test_mon ) then + \mv -f test_mon ${OSVER}bin + endif + breaksw + + case "ZPU": + setenv OS "ZPU" + setenv OSVER 1 + echo "ZPU operating system build" + echo "" + if( ! -r .zpu ) then + \rm -f .sunos + \rm -f .solaris + \rm -f .linux + \rm -f .zpu + touch .zpu + chmod 777 .zpu + make -f Makefile clean $makecmd + set result = $status + else + make -f Makefile ZPU_BUILD=1 $makecmd + set result = $status + endif + if( $result == 0 && -r test_mon ) then + \mv -f test_mon ${OSVER}bin + endif + breaksw + + default: + echo "Unknown architecture, cannot build library..." + breaksw +endsw diff --git a/ux_test/test_mon.c b/ux_test/test_mon.c new file mode 100755 index 0000000..204f668 --- /dev/null +++ b/ux_test/test_mon.c @@ -0,0 +1,548 @@ +/****************************************************************************** + * Product: ####### ####### ##### ####### # # ####### # # + * # # # # # ## ## # # ## # + * # # # # # # # # # # # # # + * # ##### ##### # # # # # # # # # + * # # # # # # # # # # # + * # # # # # # # # # # ## + * # ####### ##### # ####### # # ####### # # + * + * File: test_mon.c + * Description: A Test Harness program specifically for testing out ux + * monitor functionality. Ie the ability to add an interactive + * monitor to any application. + * + * 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 + +/* Bring in UX header files. +*/ +#include + +/* Specials for Solaris. +*/ +#if defined(SOLARIS) || defined(LINUX) || defined(ZPU) +#include +#endif + +/* Indicate that we are a C module for any header specifics. +*/ +#define TEST_MON_C + +/* Bring in local specific header files. +*/ +#include "test_mon.h" + +/****************************************************************************** + * Function: _HTML_GetHandler + * Description: Call back to override default HTML GET handler. This function + * works out what the client browser requires and tries to + * satisfy it. + * + * Returns: R_OK - Configuration obtained. + * R_FAIL - Failure, see error message. + ******************************************************************************/ +int _HTML_GetHandler( UINT nChanId, /* I: Channel Id of new con */ + UCHAR *szData, /* I: Data received by WWW */ + UINT nMonPort ) /* I: Monitor Port Number */ +{ + /* Local variables. + */ + int nCnt=0; + int nChar; + UINT nPos = 0; + UINT nTokType; + UCHAR *spEndStr; + UCHAR *spMsgBuf = NULL; + UCHAR szFileName[MAX_FILENAMELEN+1]; + char *szFunc = "_HTML_GetHandler"; + FILE *fp; + + /* Log a debug message to indicate details of this connection. + */ + Lgr(LOG_DEBUG, szFunc, + "GET Handler called: Data=%s, nChanId=%d, MonPort=%d\n", + szData, nChanId, nMonPort); + + /* Setup HTML content type. + */ + ML_Send(nChanId, "Content-type: text/html\n\n", 0); + + /* Scan the buffer for the recognised browser end of stream: + * HTTP/version. If it doesnt exist, send an error message to client + * and exit. + */ + if( (spMsgBuf=(UCHAR *)malloc((strlen(szData)*3)+1)) == NULL ) + { + /* Get out with a failure, memory exhausted. + */ + ML_Send(nChanId, "Out of Memory" + "Out of Memory, Re-Try Later/n/n", 0); + if(spMsgBuf != NULL) free(spMsgBuf); + return(R_FAIL); + } + if((spEndStr=strstr(szData, "HTTP")) != NULL) + { + /* Command we are required to understand lies between the beginning + * of the buffer and where HTTP starts, extract it. + */ + FFwdOverWhiteSpace(szData, &nPos); + strncpy(spMsgBuf, &szData[nPos], ((spEndStr - szData)-nPos)); + spMsgBuf[(spEndStr - szData)-nPos] = '\0'; + } else + { + /* Dispatch an error message to the client as their is not much + * we can do. + */ + ML_Send(nChanId, "Illegal HTML" + "The HTML that your browser issued is illegal, or it is from" + " a newer version not supported by this product\n\n", 0); + return(R_FAIL); + } + + /* Trim off the fat and open file. + */ + strcpy(szFileName, StrRTrim(spMsgBuf)); + if((fp=fopen(szFileName, "r")) == NULL) + { + sprintf(spMsgBuf, "Cannot access %s" + "

File Not Available

\n" + "The file requested (%s) cannot be accessed.\n\n", + szFileName, szFileName); + ML_Send(nChanId, spMsgBuf, 0); + return(R_FAIL); + } else + { + /* Crude but effective, read 1 byte at a time and fire it off, + * wrapped in a HTML structure. + */ + ML_Send(nChanId, "\n
\n", 0);
+        spMsgBuf[1]='\0';
+        while((nChar=fgetc(fp)) != EOF)
+        {
+        nCnt++;
+            spMsgBuf[0]=nChar;
+            ML_Send(nChanId, spMsgBuf, 1);
+        }
+        ML_Send(nChanId, "
", 0); + fclose(fp); + } + + /* All done, get out! + */ + return(R_OK); +} + +/****************************************************************************** + * Function: _HTML_ConnectCB + * Description: Call back for when an incoming WWW browser makes a connection + * with us. + * + * Returns: R_OK - Configuration obtained. + * R_FAIL - Failure, see error message. + ******************************************************************************/ +int _HTML_ConnectCB( UINT nChanId, /* I: Channel Id of new con */ + UINT nMonPort ) /* I: Monitor Port Number */ +{ + /* Local variables. + */ + char *szFunc = "_HTML_ConnectCB"; + + /* Log a debug message to indicate details of this connection. + */ + Lgr(LOG_DEBUG, szFunc, "New Connection: ChanID=%d, MonPort=%d\n", + nChanId, nMonPort); + + /* All done, get out! + */ + return; +} + +/****************************************************************************** + * Function: _HTML_DisconnectCB + * Description: Call back for when an existing WWW browser connection ceases + * to exist. + * + * Returns: R_OK - Configuration obtained. + * R_FAIL - Failure, see error message. + ******************************************************************************/ +int _HTML_DisconnectCB( UINT nChanId, /* I: Channel Id of new con */ + UINT nMonPort ) /* I: Monitor Port Number */ +{ + /* Local variables. + */ + char *szFunc = "_HTML_DisconnectCB"; + + /* Log a debug message to indicate details of this connection. + */ + Lgr(LOG_DEBUG, szFunc, "Connection Closed: ChanID=%d, MonPort=%d\n", + nChanId, nMonPort); + + /* All done, get out! + */ + return; +} + +/****************************************************************************** + * Function: _NL_HelpHandler + * Description: Call back to implement a HELP feature in the natural + * language command interface. This command basically lists + * global or specific help according to the arguments of the + * command and fires it back to the client. + * + * Returns: R_OK - Configuration obtained. + * R_FAIL - Failure, see error message. + ******************************************************************************/ +int _NL_HelpHandler( UINT nChanId, /* I: Channel Id of new con */ + UCHAR *szData, /* I: Data received by WWW */ + UINT nMonPort ) /* I: Monitor Port Number */ +{ + /* Local variables. + */ + char *szFunc = "_NL_HelpHandler"; + + /* Log a debug message to indicate details of this connection. + */ + Lgr(LOG_DEBUG, szFunc, + "Help Handler called: Data=%s, nChanId=%d, MonPort=%d\n", + szData, nChanId, nMonPort); + + /* All done, get out! + */ + return(R_OK); +} + +/****************************************************************************** + * Function: _NL_ConnectCB + * Description: Call back for when an incoming WWW browser makes a connection + * with us. + * + * Returns: R_OK - Configuration obtained. + * R_FAIL - Failure, see error message. + ******************************************************************************/ +int _NL_ConnectCB( UINT nChanId, /* I: Channel Id of new con */ + UINT nMonPort ) /* I: Monitor Port Number */ +{ + /* Local variables. + */ + char *szFunc = "_NL_ConnectCB"; + + /* Log a debug message to indicate details of this connection. + */ + Lgr(LOG_DEBUG, szFunc, "New Connection: ChanID=%d, MonPort=%d\n", + nChanId, nMonPort); + + /* All done, get out! + */ + return; +} + +/****************************************************************************** + * Function: _NL_DisconnectCB + * Description: Call back for when an existing WWW browser connection ceases + * to exist. + * + * Returns: R_OK - Configuration obtained. + * R_FAIL - Failure, see error message. + ******************************************************************************/ +int _NL_DisconnectCB( UINT nChanId, /* I: Channel Id of new con */ + UINT nMonPort ) /* I: Monitor Port Number */ +{ + /* Local variables. + */ + char *szFunc = "_NL_DisconnectCB"; + + /* Log a debug message to indicate details of this connection. + */ + Lgr(LOG_DEBUG, szFunc, "Connection Closed: ChanID=%d, MonPort=%d\n", + nChanId, nMonPort); + + /* All done, get out! + */ + return; +} + +/****************************************************************************** + * Function: GetConfig + * Description: Get configuration information from the OS or command line + * flags. + * + * Returns: R_OK - Configuration obtained. + * R_FAIL - Failure, see error message. + ******************************************************************************/ +int GetConfig( int argc, /* I: CLI argument count */ + UCHAR **argv, /* I: CLI argument contents */ + char **envp, /* I: Environment variables */ + UCHAR *szErrMsg ) /* O: Any generated error message */ +{ + /* Local variables. + */ + int nReturn = R_OK; + FILE *fp; + UCHAR *szFunc = "GetConfig"; + + /* See if the user wishes to use a logfile? + */ + if( GetCLIParam(argc, argv, FLG_LOGFILE, T_STR, TMON.szLogFile, + MAX_LOGFILELEN, FALSE) == R_OK ) + { + /* Check to see if the filename is valid. + */ + if((fp=fopen(TMON.szLogFile, "a")) == NULL) + { + sprintf(szErrMsg, "Cannot write to logfile (%s)", TMON.szLogFile); + return(R_FAIL); + } + + /* Close the file as test complete. + */ + fclose(fp); + } else + { + /* Set logfile to a default, dependant on OS. + */ + strcpy(TMON.szLogFile, DEF_LOGFILE); + } + + /* Get log mode from command line. + */ + if(GetCLIParam(argc, argv, FLG_LOGMODE, T_INT, (UCHAR *)&TMON.nLogMode, + 0, 0) == R_OK) + { + /* Check the validity of the mode. + */ + if((TMON.nLogMode < LOG_OFF || TMON.nLogMode > LOG_FATAL) && + TMON.nLogMode != LOG_CONFIG) + { + sprintf(szErrMsg, "Illegal Logger mode (%d)", TMON.nLogMode); + return(R_FAIL); + } + } else + { + /* Setup default log mode. + */ + TMON.nLogMode = LOG_DEBUG; + } + + /* Get the port to be used for HTML monitoring. + */ + if(GetCLIParam(argc, argv, FLG_HTMLPORT, T_INT, (UCHAR *)&TMON.nHtmlPort, + 0, 0) == R_OK) + { + /* Check the validity of the port. + */ + if((TMON.nHtmlPort < 2000 || TMON.nHtmlPort > 10000)) + { + sprintf(szErrMsg, "Illegal HTML TCP Port (%d)", TMON.nHtmlPort); + return(R_FAIL); + } + } else + { + /* Setup default port. + */ + TMON.nHtmlPort = DEF_HTML_PORT; + } + + /* Get the port to be used for HTML monitoring. + */ + if(GetCLIParam(argc, argv, FLG_NLPORT, T_INT, (UCHAR *)&TMON.nNLPort, + 0, 0) == R_OK) + { + /* Check the validity of the port. + */ + if((TMON.nNLPort < 2000 || TMON.nNLPort > 10000)) + { + sprintf(szErrMsg, "Illegal Natural Language TCP Port (%d)", + TMON.nNLPort); + return(R_FAIL); + } + } else + { + /* Setup default port. + */ + TMON.nNLPort = DEF_NL_PORT; + } + + /* Finished, get out! + */ + return( nReturn ); +} + +/****************************************************************************** + * Function: TMONInit + * Description: Initialisation of variables, functionality, communications etc. + * + * Returns: VDWD_OK - Initialised successfully. + * VDWD_FAIL - Failure, see error message. + ******************************************************************************/ +int TMONInit( UCHAR *szErrMsg ) /* O: Generated error message */ +{ + /* Local variables. + */ + char *szFunc = "TMONInit"; + + /* Setup logger mode. + */ + Lgr(LOG_CONFIG, LGM_FLATFILE, TMON.nLogMode, TMON.szLogFile); + + /* Initialise Socket Library. + */ + if(SL_Init(TMON_SRV_KEEPALIVE, (UCHAR *)NULL) != R_OK) + { + sprintf(szErrMsg, "SL_Init failed"); + Lgr(LOG_DEBUG, szFunc, szErrMsg); + return(R_FAIL); + } + + /* Initialise Monitor Library for HTML servicing. + */ + if(ML_Init(TMON.nHtmlPort, MON_SERVICE_HTML, "Test Monitor Program (HTML)", + _HTML_ConnectCB, _HTML_DisconnectCB, NULL) == R_FAIL) + { + sprintf(szErrMsg, "ML_Init failed for HTML service"); + Lgr(LOG_DEBUG, szFunc, szErrMsg); + return(R_FAIL); + } + + /* Initialise Monitor Library for Natural Language servicing. + */ + if(ML_Init(TMON.nNLPort, MON_SERVICE_NL, "Test Monitor Program (NL)", + _NL_ConnectCB, _NL_DisconnectCB, NULL) == R_FAIL) + { + sprintf(szErrMsg, "ML_Init failed for NL service"); + Lgr(LOG_DEBUG, szFunc, szErrMsg); + return(R_FAIL); + } + + /* Add test commands for HTML. + */ + ML_AddMonCommand(TMON.nHtmlPort, MC_HTMLGET, _HTML_GetHandler); + + /* Add test commands for Natural Language. + */ + ML_AddMonCommand(TMON.nNLPort, MC_NLHELP, _NL_HelpHandler); + + /* All done, lets get out. + */ + return(R_OK); +} + +/****************************************************************************** + * Function: TMONClose + * Description: Function to perform closure of all used resources within the + * program. + * + * Returns: R_OK - Closed successfully. + * R_FAIL - Failure, see error message. + ******************************************************************************/ +int TMONClose( UCHAR *szErrMsg ) /* O: Generated error message */ +{ + /* Local variables. + */ + char *szFunc = "TMONClose"; + + /* Call monitor library to close and tidy up. + */ + if(ML_Exit(NULL) == R_FAIL) + { + Lgr(LOG_DEBUG, szFunc, "Failed to close Monitor Library"); + } + + /* Exit with success. + */ + return(R_OK); +} + +/****************************************************************************** + * Function: main + * Description: Entry point into the Monitor facility Test program. Basic + * purpose is to invoke intialisation, enter the main program + * loop and finally tidy up and close down. + * + * Returns: 0 - Program completed successfully without errors. + * -1 - Program terminated with errors, see logged message. + ******************************************************************************/ +int main( int argc, /* I: Count of available arguments */ + char **argv, /* I: Array of arguments */ + char **envp ) /* I: Array of environment parameters */ +{ + /* Local variables. + */ + UCHAR szErrMsg[MAX_ERRMSG_LEN]; + UCHAR *szFunc = "main"; + + /* Bring in any configuration parameters passed on the command line etc. + */ + if( GetConfig(argc, (UCHAR **)argv, envp, szErrMsg) == R_FAIL ) + { + printf( "%s\n" + "Usage: %s \n" + ": -l\n" + " -m\n" + " -html_port\n" + " -nl_port\n", + szErrMsg, argv[0]); + } + + /* Initialise variables, communications etc. + */ + if( TMONInit(szErrMsg) == R_FAIL ) + { + /* Log an error message to indicate reason for failure. + */ + Lgr(LOG_DIRECT, szFunc, "%s: %s", argv[0], szErrMsg); + exit(-1); + } + + /* Do nothing basically, where testing monitor functionality, so just loop. + */ + while(TRUE) + { + SL_Poll(DEF_POLLTIME); + } + + /* Perform close down of used facilities ready for exit. + */ + if( TMONClose(szErrMsg) == R_FAIL ) + { + /* Log an error message to indicate reason for failure. + */ + Lgr(LOG_DIRECT, szFunc, "%s: %s", argv[0], szErrMsg); + exit(-1); + } + + /* All done, go bye bye's. + */ +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) + exit; +#endif +#if defined(_WIN32) + return(0); +#endif +} diff --git a/ux_test/test_mon.h b/ux_test/test_mon.h new file mode 100755 index 0000000..52513ef --- /dev/null +++ b/ux_test/test_mon.h @@ -0,0 +1,98 @@ +/****************************************************************************** + * Product: ####### ####### ##### ####### # # ####### # # + * # # # # # ## ## # # ## # + * # # # # # # # # # # # # # + * # ##### ##### # # # # # # # # # + * # # # # # # # # # # # + * # # # # # # # # # # ## + * # ####### ##### # ####### # # ####### # # + * + * File: test_mon.h + * Description: Header file for declaration of structures, datatypes etc for + * the ux library monitor testing program. + * 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 . + ******************************************************************************/ + +/* Ensure file is only included once - avoid compile loops. +*/ +#ifndef TEST_MON_H +#define TEST_MON_H + +/* Definitions for maxims etc. +*/ +#define MAX_ERRMSG_LEN 256 +#define MAX_LOGFILELEN 256 + +/* Definitions for defaults. +*/ +#define DEF_HTML_PORT 9000 +#define DEF_NL_PORT 9001 +#define DEF_POLLTIME 1000 +#if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(ZPU) +#define DEF_LOGFILE "/tmp/test_mon.log" +#endif +#if defined(_WIN32) +#define DEF_LOGFILE "\\TEST_MON.LOG" +#endif + +/* Define constants etc. +*/ +#define TMON_SRV_KEEPALIVE 1000 /* TCP/IP keep alive */ + +/* Define command line flags. +*/ +#define FLG_LOGFILE "-l" +#define FLG_LOGMODE "-m" +#define FLG_HTMLPORT "-html_port" +#define FLG_NLPORT "-nl_port" + +/* Monitor commands for Natural Language interface. +*/ +#define MC_NLHELP "HELP" + +/* Maxims. +*/ +#define MAX_FILENAMELEN 80 + +/* Globals (yuggghhh!). +*/ +typedef struct { + int nHtmlPort; + int nNLPort; + UINT nLogMode; + UCHAR szLogFile[MAX_LOGFILELEN]; +} TMON_GLOBALS; + +/* Declare any globals required by the daemon, or any specifics to the + * C module. +*/ +#if defined(TEST_MON_C) + static TMON_GLOBALS TMON={9000, 9001, LOG_DEBUG, ""}; +#endif + +/* Prototypes for functions. +*/ +int GetConfig( int, UCHAR **, char **, UCHAR * ); +int TMONInit( UCHAR * ); +int TMONClose( UCHAR * ); +int main( int, char **, char ** ); + +#endif /* TEST_MON_H */