/*****************************************************************************/ #ifdef COMMENTS_WITH_COMMENTS /* rawLIB.c Collection of routines for WASD persistent RawSocket applications (CGIplus scripts). Could have named it RSLIB (raw socket library) but RS means different things to different people :-} A "raw" socket is a variant of, and is heavily based on, the WASD WebSocket infrastructure. It allows a service (listening host and port) to accept a connection and immediately activate a configured WASD CGIplus script (in much the same manner as a standard WebSocket script) to service that connection. Full-duplex, asynchronous, bidirectional input/output is supported. While being referred to as a "socket", and in the module as a "RawSocket", it of course is not [BSD or any other abstraction] network socket communication, but a read/write abstraction via mailbox I/O. Input is a stream of octets; output a stream of octets. To these routines the streams are opaque. The "RawSocket" is just WASD nomenclature for WebSocket-style scripting without the WebSocket protocol. These routines allow AST-driven, multi-client RawSocket applications. These are *much* less complex than those in wsLIB as there is no protocol implemented and no message/frame queue. RAWSOCKET BASICS ---------------- The script when activated enters a CGIplus wait-service-wait loop using the standard CGIplus mechanism. When a request activates the script it issues a "100 Continue" CGI/HTTP header to signal the server that the request is being processed. This header is NOT relayed to the client. The request parameters are available via the usual CGI variables. The script can then read and write to/from the RAWSOCKET_INPUT and RAWSOCKET_OUTPUT devices respectively. The end-of-script is indicated using the same WebSocket EOF to the mailbox. The script activation can be a long-lived as required and the server will not run a request down for any timeout reason. REQUEST SYNCHRONISATION ----------------------- rawLIB start-of-request synchronisation is based on the wait-process-EOF-wait loop model used by CGIlib, where RawLibCgiVar("") or RawLibCgiVarNull("") are used to wait and continue when a new request is available. The example raw [web]socket scripts use this approach by default. rawLIB also provides an AST-driven model. The RawLibOnNextRequest() function should be called once during script initialisation specifying the address of the function to be notified of a new request. At that point the CGI variables become available and remain so until the script issues the CGIplus EOF sentinal, and the RawLibCgiVar..() family of functions can be used to retrieve the CGI variable values. This asynchronous and the synchronous RawLibCgiVar("") approaches to request synchronisation are mutually exclusive and once one is used using the other results in a bugcheck. CGI VARIABLES ------------- CGI variables using the RawLibCgiVar..() functions are only available after the next request has been indicated and before the CGIplus EOF sentinal is signalled. Any that are required for further processing need to be copied into separate storage from the pointer returned by the RawLibCgiVar..() functions. With the request synchronisation provided by RawLibCgiVar("") this is usually obvious enough. With the AST-driven request synchronisation provided by RawLibOnNextRequest() it might be less obvious. In any case the RawLibCgiVarAvailable() function can be used to test whether CGI variable are available at that time or not. FUNCTIONS --------- struct RawLibStruct* RawLibCreate (void *UserDataPtr, void *DestroyFunction) Allocates a RawSocket structure, sets the user data pointer to the supplied address, and returns a pointer to the new structure. All subsequent associated websocket processing requires that pointer. struct RawLibStruct* RawLibNext (struct RawLibStruct **RawLibCtx) Step through the list of structures. RawLibCtx is the address of pointer to RawLibStruct used to hold the context. Set to NULL to initialise. Returns non-null pointers for each structure in the list, then NULL when exhausted. Care must be exercised that multiple calls are not preempted by list modification (i.e. use within AST delivery or with ASTs disabled). int RawLibMsgDsc (struct RawLibStruct *rawptr, struct dsc$descriptor_s *MsgDsc); Sets the supplied descriptor to the latest error string. int RawLibMsgLineNumber (struct RawLibStruct *rawptr) Return the wsLIB source code line at which the last message occured. char* RawLibMsgString (struct RawLibStruct *rawptr) Returns a pointer to the latest error string. int RawLibOpen (struct RawLibStruct *rawptr, void *AstFunction) Using the device names from the CGI variables RAWSOCKET_INPUT and RAWSOCKET_OUTPUT assign channels in preparation for asynchronous I/O. The AST function is called at RawSocket close. int RawLibClose (struct RawLibStruct* rawptr) Shut the RawSocket down. int RawLibIsClosed (struct RawLibStruct* rawptr) Return true if the RawSocket has been closed. int RawLibConnected (struct RawLibStruct *rawptr) Return true if the input/output channels are connected. int RawLibRead (struct RawLibStruct *rawptr, char *DataPtr, int DataSize, void *AstFunction) Read data from the RawSocket into the supplied buffer. Function is non-blocking if AST function is supplied, otherwise blocking. The buffer must continue to exist until the read completes. When the read completes the status can be returned by RawLibReadStatus(), the read count by RawLibReadCount(), a pointer to the data buffer using RawLibReadData(), and a pointer to the internal read string descriptor using RawLibReadDataDsc(). int RawLibReadDsc (struct RawLibStruct *rawptr, struct dsc$descriptor_s *DataDsc, void *AstFunction) Read data from the websocket into the buffer described by DataDsc. Function is non-blocking if AST function is supplied, otherwise blocking. When completed ReadDsc (if supplied) is modified to reflect the read. int RawLibReadStatus (struct RawLibStruct *rawptr) Return the status value of the most recent read. int RawLibReadCount (struct RawLibStruct *rawptr) Return the number of bytes in the most recent read. char* RawLibReadData (struct RawLibStruct *rawptr) Return a pointer to char of the most recent read buffer. struct dsc$descriptor_s* RawLibReadDataDsc (struct RawLibStruct *rawptr) Return a pointer to the internal descriptor for the most recent read. ulong* RawLibReadTotal (struct RawLibStruct *rawptr) Return a pointer to a quadword containing the total number of bytes read. void RawLibResetMsg (struct RawLibStruct *rawptr) Reset the latest message data. unsigned int RawLibTime (); Return the current wsLIB time in seconds (i.e. C-RTL, Unix time). int RawLibWrite (struct RawLibStruct *rawptr, char *DataPtr, int DataCount, void *AstFunction) Write the supplied data to the websocket. Function is non-blocking if AST function is supplied, otherwise blocking. The data must continue to exist until the write completes. int RawLibWriteDsc (struct RawLibStruct *rawptr, struct dsc$descriptor_s *DataDsc, void *AstFunction) Write the data supplied by the descriptor to the websocket. Function is non-blocking if AST function is supplied, otherwise blocking. int RawLibWriteStatus (struct RawLibStruct *rawptr) Return the status value of the most recent write. int RawLibWriteCount (struct RawLibStruct *rawptr) Return the number of bytes in the most recent write. struct dsc$descriptor_s* RawLibWriteDataDsc (struct RawLibStruct *rawptr) Return a pointer to the internal descriptor for the most recent write. ulong* RawLibWriteTotal (struct RawLibStruct *rawptr) Return a pointer to a quadword containing the total number of bytes written. void RawLibSetUserData (struct RawLibStruct *rawptr, void *UserDataPtr) Set the RawSocket structure to point to the supplied user data. void* RawLibGetUserData (struct RawLibStruct *rawptr) Return the current pointer to the supplied user data. void* RawLibSetCallout (struct RawLibStruct *rawptr, void *AstFunction) Set/reset the callout response AST. Returns the previous callout pointer. void* RawLibSetMsgCallback (struct RawLibStruct *rawptr, void *CallbackFunction) Set/reset the rawLIB error reporting callback. Returns the previous callback pointer. The error callback function is activated with a pointer to the RawLib data structure that has the MsgLineNumber and timestamp set and provides a descriptive string. void RawLibSetIdleSecs (struct RawLibStruct *rawptr, int IdleSecs); Set number of seconds before a RawSocket is considered idle and closed. If a RawSocket is not specified then set global value. void RawLibSetLifeSecs (int LifeSecs); Set number of seconds before the application is considered idle and exited. void RawLibSetReadSecs (struct RawLibStruct *rawptr, int IdleSecs); Set number of seconds to wait for a read from a RawSocket before close. If a RawSocket is not specified then set global value. void* RawLibSetWakeCallback (struct RawLibStruct *rawptr, void *CallbackFunction, int WakesSecs) Set/reset the wsLIB periodic wakeup callback. Returns the previous callback pointer. Set the number of seconds between wakeup calls (zero defaults). void RawLibWatchScript (struct RawLibStruct *rawptr, char *SourceModuleName, int SourceLineNumber, char *FormatString, ...) Outputs the $FAO-formatted string to the WASD WATCH [x]Script item. COPYRIGHT --------- Copyright (C) 2016-2020 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. VERSION HISTORY --------------- 01-DEC-2016 MGD initial (heavily based on wsLIB) */ #endif /* COMMENTS_WITH_COMMENTS */ /*****************************************************************************/ #define SOFTWAREVN "1.0.0" #define SOFTWARENM "RAWLIB" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rawlib.h" /* from libmsg.h */ #define LIB$_INVSTRDES 1409572 #ifndef EFN$C_ENF # define EFN$C_ENF 128 /* Event No Flag (no stored state) */ #endif #define AGN$M_READONLY 0x01 #define AGN$M_WRITEONLY 0x02 #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) !(((x) & STS$M_SUCCESS)) #define RAWLIB_STRUCT_BUFFER_SIZE 4096 #define FI_LI "RAWLIB", __LINE__ #if 1 #define WATCH_RAWLIB if(rawptr->WatchScript)RawLibWatchScript #else #define WATCH_RAWLIB if(0)RawLibWatchScript #endif /* used by RAWLIBCL.C */ static int EfnWait, EfnNoWait; static int CalloutDone = 0, CgiPlusEofLength = 0, CgiPlusEotLength = 0, CgiPlusEscLength = 0, CgiVariablesAvailable = 0, StructBufferLength = 0, StructBufferSize = 0, WwwPrefix = 0; static ushort CgiPlusInChannel; static ulong CurrentTime, WatchDogWakeTime; static ulong CurrentBinTime [2]; #define DEFAULT_WATCHDOG_IDLE_SECS 120 #define DEFAULT_WATCHDOG_LIFE_SECS 120 #define DEFAULT_WATCHDOG_READ_SECS 60 #define DEFAULT_WATCHDOG_WAKE_SECS 60 static ulong WatchDogIdleSecs = DEFAULT_WATCHDOG_IDLE_SECS, WatchDogLifeSecs = DEFAULT_WATCHDOG_LIFE_SECS, WatchDogReadSecs = DEFAULT_WATCHDOG_READ_SECS, WatchDogWakeSecs = DEFAULT_WATCHDOG_WAKE_SECS; static char *CgiPlusEofPtr = NULL, *CgiPlusEotPtr = NULL, *CgiPlusEscPtr = NULL, *NextVarNamePtr = NULL, *StructBufferPtr = NULL; static struct RawLibStruct *ListHead; static void (*RequestSynchronisation)() = NULL, (*WakeCallbackFunction)() = NULL; static char SoftwareID [] = SOFTWAREID; #define SYI$_VERSION 4096 int sys$getsyi(__unknown_params); /*** #define __VAX #undef __ALPHA ***/ /*****************************************************************************/ /* Initialise the library. */ void RawLibInit () { static char GetSyiVer [8]; static struct { ushort buf_len; ushort item; void *buf_addr; void *ret_len; } SyiItem [] = { { sizeof(GetSyiVer)-1, SYI$_VERSION, &GetSyiVer, 0 }, { 0,0,0,0 } }; int status, VersionInteger; /*********/ /* begin */ /*********/ /* just the once! */ if (EfnWait) return; status = sys$getsyiw (0, 0, 0, &SyiItem, 0, 0, 0); if (VMSnok (status)) RawLibExit (NULL, FI_LI, status); VersionInteger = ((GetSyiVer[1]-48) * 100) + ((GetSyiVer[3]-48) * 10); if (GetSyiVer[4] == '-') VersionInteger += GetSyiVer[5]-48; if (VersionInteger >= 700) EfnWait = EfnNoWait = EFN$C_ENF; else { if (VMSnok (status = lib$get_ef (&EfnWait))) RawLibExit (NULL, FI_LI, status);; if (VMSnok (status = lib$get_ef (&EfnNoWait))) RawLibExit (NULL, FI_LI, status);; } WatchDog (); } /*****************************************************************************/ /* Return a pointer to the wsLIB version string. */ char* RawLibVersion () { /*********/ /* begin */ /*********/ return (SoftwareID); } /*****************************************************************************/ /* Sanity check the incoming request. Provide error or continue/upgrade response. Allocate a RawSocket I/O structure and set the internal user data storage. Insert at the head of the list. Return a pointer to the allocated structure. Can only WATCH_RAWLIB after RawLibOpen(). */ struct RawLibStruct* RawLibCreate ( void *UserDataPtr, void *DestroyFunction ) { int astatus, SecRawSocketVersion; char *cptr, *sptr; struct RawLibStruct *rawptr; /*********/ /* begin */ /*********/ if (!EfnWait) RawLibInit (); astatus = sys$setast (0); rawptr = (struct RawLibStruct*) calloc (1, sizeof(struct RawLibStruct)); if (!rawptr) RawLibExit (NULL, FI_LI, vaxc$errno); if ((cptr = getenv ("WASD_RAWLIB_WATCH_LOG")) != NULL) if ((rawptr->WatchLog = fopen (cptr, "w", "shr=get")) == NULL) RawLibExit (NULL, FI_LI, vaxc$errno); if (cptr = RawLibCgiVarNull("RAWSOCKET_INPUT_MRS")) rawptr->InputMrs = atoi(cptr); else RawLibExit (NULL, FI_LI, SS$_BUGCHECK); if (cptr = RawLibCgiVarNull("RAWSOCKET_OUTPUT_MRS")) rawptr->OutputMrs = atoi(cptr); else RawLibExit (NULL, FI_LI, SS$_BUGCHECK); /* connection acceptance response */ fprintf (stdout, "Status: 101 Switching Protocols\r\n\r\n"); fflush (stdout); rawptr->UserDataPtr = UserDataPtr; rawptr->DestroyAstFunction = DestroyFunction; rawptr->NextPtr = ListHead; ListHead = rawptr; if (astatus == SS$_WASSET) sys$setast (1); return (rawptr); } /*****************************************************************************/ /* Remove from the list and free the allocated memory. Return any user data. */ static void Destroy (struct RawLibStruct *rawptr) { int astatus, cnt, status; struct RawLibStruct *wslptr; void *UserDataPtr; FILE *WatchLog; /*********/ /* begin */ /*********/ if (!rawptr) return; WATCH_RAWLIB (rawptr, FI_LI, "DESTROY"); WatchLog = rawptr->WatchLog; astatus = sys$setast (0); UserDataPtr = rawptr->UserDataPtr; if (rawptr->MsgStringSize) free (rawptr->MsgStringPtr); if ((wslptr = ListHead) == rawptr) ListHead = wslptr->NextPtr; else { while (wslptr->NextPtr != rawptr) wslptr = wslptr->NextPtr; wslptr->NextPtr = rawptr->NextPtr; } sys$dassgn (rawptr->OutputChannel); free (rawptr); if (astatus == SS$_WASSET) sys$setast (1); if (WatchLog) fclose (WatchLog); } /*****************************************************************************/ /* Step through the list of structures. RawLibCtx is the address of a pointer used to hold the context. Set to NULL to initialise. Returns non-null pointers for each structure in the list, then NULL when list exhausted, to begin non-null pointers again. Care must be exercised that multiple calls are not preempted by a list modification (i.e. use within AST delivery or with ASTs disabled). */ struct RawLibStruct* RawLibNext (struct RawLibStruct **RawLibCtx) { int astatus; struct RawLibStruct *rawptr, *wslptr; /*********/ /* begin */ /*********/ astatus = sys$setast (0); /* let's be overcautious and make sure it's still in the list! */ if (rawptr = *RawLibCtx) { for (wslptr = ListHead; wslptr; wslptr = wslptr->NextPtr) if (wslptr == rawptr) break; if (!wslptr) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); *RawLibCtx = rawptr->NextPtr; } else *RawLibCtx = ListHead; if (astatus == SS$_WASSET) sys$setast (1); return (*RawLibCtx); } /*****************************************************************************/ /* Using the device names from the CGI variables RAWSOCKET_INPUT and RAWSOCKET_OUTPUT assign channels in preparation for asynchronous I/O. */ int RawLibOpen (struct RawLibStruct *rawptr) { int status; char *cptr, *sptr, *zptr; $DESCRIPTOR (MbxDsc, ""); /*********/ /* begin */ /*********/ rawptr->ReadDataDsc.dsc$b_class = rawptr->ReadResultDsc.dsc$b_class = rawptr->WriteDataDsc.dsc$b_class = DSC$K_CLASS_S; rawptr->ReadDataDsc.dsc$b_dtype = rawptr->ReadResultDsc.dsc$b_dtype = rawptr->WriteDataDsc.dsc$b_dtype = DSC$K_DTYPE_T; if (!(cptr = RawLibCgiVarNull("RAWSOCKET_INPUT"))) return (SS$_BUGCHECK); zptr = (sptr = rawptr->InputDevName) + sizeof(rawptr->InputDevName)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; rawptr->InputDevDsc.dsc$b_class = DSC$K_CLASS_S; rawptr->InputDevDsc.dsc$b_dtype = DSC$K_DTYPE_T; rawptr->InputDevDsc.dsc$a_pointer = rawptr->InputDevName; rawptr->InputDevDsc.dsc$w_length = sptr - rawptr->InputDevName; if (!(cptr = RawLibCgiVarNull("RAWSOCKET_OUTPUT"))) return (SS$_BUGCHECK); zptr = (sptr = rawptr->OutputDevName) + sizeof(rawptr->OutputDevName)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; rawptr->OutputDevDsc.dsc$b_class = DSC$K_CLASS_S; rawptr->OutputDevDsc.dsc$b_dtype = DSC$K_DTYPE_T; rawptr->OutputDevDsc.dsc$a_pointer = rawptr->OutputDevName; rawptr->OutputDevDsc.dsc$w_length = sptr - rawptr->OutputDevName; status = sys$assign (&rawptr->InputDevDsc, &rawptr->InputChannel, 0, 0, AGN$M_READONLY); if (VMSnok (status)) return (status); status = sys$assign (&rawptr->OutputDevDsc, &rawptr->OutputChannel, 0, 0, AGN$M_WRITEONLY); if (VMSnok (status)) { sys$dassgn (rawptr->InputChannel); rawptr->InputChannel = 0; return (status); } if (VMSnok (status)) { sys$dassgn (rawptr->InputChannel); sys$dassgn (rawptr->OutputChannel); rawptr->InputChannel = rawptr->OutputChannel = 0; return (status); } if (!(rawptr->WatchScript = (rawptr->WatchLog != NULL))) rawptr->WatchScript = (RawLibCgiVarNull("WATCH_SCRIPT") != NULL); WATCH_RAWLIB (rawptr, FI_LI, "OPEN !AZ", SOFTWAREID); return (SS$_NORMAL); } /*****************************************************************************/ /* Initiate a close from the application end. */ void RawLibClose (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ WATCH_RAWLIB (rawptr, FI_LI, "CLOSE closed:!UL", rawptr->RawSocketClosed); rawptr->RawSocketClosed = 1; Shut (rawptr); } /*****************************************************************************/ /* Return true if the RawSocket has been closed. */ int RawLibIsClosed (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->RawSocketClosed); } /*****************************************************************************/ /* Shutdown the websocket (this is different to the close handshake). Cancel any outstanding I/O and deassign channels. Call any supplied closure AST function. This function call be called multiple times before full shutdown accomplished. Returns success status when finally shut, abort if not (fully) shut (yet). */ int Shut (struct RawLibStruct *rawptr) { int astatus, status; /*********/ /* begin */ /*********/ if (rawptr->WatchLog) WATCH_RAWLIB (rawptr, FI_LI, "Shut() destroy:!8XL(!UL) shut:!UL/!UL closed:!UL input:!UL output:!UL", rawptr->DestroyAstFunction, rawptr->DestroyAstFunction == &Destroy, rawptr->RawSocketShut, rawptr->ShutCount, rawptr->RawSocketClosed, rawptr->QueuedInput, rawptr->QueuedOutput); if (rawptr->DestroyAstFunction == &Destroy) return (SS$_NORMAL); if (!rawptr->RawSocketShut) { if (rawptr->QueuedInput) sys$cancel (rawptr->InputChannel); /* advise the server that the connection is being shut down */ astatus = sys$setast (0); status = sys$qio (EfnNoWait, rawptr->OutputChannel, IO$_WRITEOF | IO$M_NORSWAIT, 0, WriteEofAst, rawptr, 0, 0, 0, 0, 0, 0); if (VMSok(status)) rawptr->QueuedOutput++; if (astatus == SS$_WASSET) sys$setast (1); /* can be shut without having been closed (e.g. network error) */ rawptr->RawSocketShut = rawptr->RawSocketClosed = 1; } else if (rawptr->QueuedOutput) if (rawptr->ShutCount++ > rawptr->QueuedOutput) sys$cancel (rawptr->OutputChannel); /* if outstanding I/O */ if (rawptr->QueuedInput || rawptr->QueuedOutput) return (SS$_ABORT); /* deassign of output channel is handled during destroy */ sys$dassgn (rawptr->InputChannel); rawptr->InputChannel = 0; /* first queue any client's destruction code */ if (rawptr->DestroyAstFunction) sys$dclast (rawptr->DestroyAstFunction, rawptr, 0, 0); /* then queue the rawLIB structure destruction */ sys$dclast ((rawptr->DestroyAstFunction = &Destroy), rawptr, 0, 0); return (SS$_NORMAL); } /*****************************************************************************/ /* Server has been advised of the closing connection (or t least the EOF has been queued). Decrement the queued I/O counter and then recall the shut function. */ static void WriteEofAst (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ if (rawptr->QueuedOutput) rawptr->QueuedOutput--; else RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); Shut (rawptr); } /*****************************************************************************/ /* Return true if both input and output channel connected. */ int RawLibConnected (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->InputChannel && rawptr->OutputChannel); } /*****************************************************************************/ /* Read data from the RawSocket client using a descriptor to describe the input buffer. */ int RawLibReadDsc ( struct RawLibStruct *rawptr, struct dsc$descriptor_s *DataDsc, struct dsc$descriptor_s *ResultDsc, void *AstFunction ) { int status; struct dsc$descriptor_s ScratchDsc; /*********/ /* begin */ /*********/ if (DataDsc->dsc$b_class != DSC$K_CLASS_S && DataDsc->dsc$b_dtype != DSC$K_DTYPE_T) return (LIB$_INVSTRDES); rawptr->ReadDataDscPtr = DataDsc; rawptr->ReadResultDscPtr = ResultDsc; status = RawLibRead (rawptr, DataDsc->dsc$a_pointer, DataDsc->dsc$w_length, AstFunction); return (status); } /*****************************************************************************/ /* Read RawSocket data from RAWSOCKET_INPUT. If an AST function is supplied then the read is asynchronous, otherwise blocking. When the read is complete the status can be returned by RawLibReadStatus(), the read count by RawLibReadCount(), a pointer to the data buffer using RawLibReadData(), and a pointer to the read string descriptor with RawLibReadDataDsc(). */ int RawLibRead ( struct RawLibStruct *rawptr, char *DataPtr, int DataSize, void *AstFunction ) { int astatus, status; struct RawLibIoStruct *ioptr; /*********/ /* begin */ /*********/ WATCH_RAWLIB (rawptr, FI_LI, "READ size:!UL", DataSize); if (rawptr->RawSocketClosed) { MsgCallback (rawptr, __LINE__, SS$_SHUT, "can't read; closed"); rawptr->ReadStatus = SS$_SHUT; if (AstFunction) ((void(*)())AstFunction)(rawptr); return (SS$_SHUT); } /* if descriptor is not initialised (by not using RawLibReadDsc()) */ if (!rawptr->ReadDataDscPtr) { rawptr->ReadDataDscPtr = &rawptr->ReadDataDsc; rawptr->ReadDataDscPtr->dsc$b_class = DSC$K_CLASS_S; rawptr->ReadDataDscPtr->dsc$b_dtype = DSC$K_DTYPE_T; rawptr->ReadDataDscPtr->dsc$a_pointer = DataPtr; rawptr->ReadDataDscPtr->dsc$w_length = DataSize; } ioptr = calloc (1, sizeof(struct RawLibIoStruct)); ioptr->RawLibPtr = rawptr; ioptr->DataPtr = DataPtr; ioptr->DataSize = DataSize; ioptr->DataDscPtr = rawptr->ReadDataDscPtr; ioptr->ResultDscPtr = rawptr->ReadResultDscPtr; if (ioptr->AstFunction = AstFunction) { /* asynchronous read */ astatus = sys$setast (0); status = sys$qio (EfnNoWait, rawptr->InputChannel, IO$_READLBLK | IO$M_STREAM | IO$M_WRITERCHECK, &ioptr->IOsb, ReadDataAst, ioptr, DataPtr, DataSize, 0, 0, 0, 0); if (VMSok(status)) rawptr->QueuedInput++; if (astatus == SS$_WASSET) sys$setast (1); } else { /* synchronous read */ status = sys$qiow (EfnWait, rawptr->InputChannel, IO$_READLBLK | IO$M_STREAM | IO$M_WRITERCHECK, &ioptr->IOsb, 0, 0, DataPtr, DataSize, 0, 0, 0, 0); if (VMSok(status)) { if (VMSok(status)) status = ioptr->IOsb.iosb$w_status; rawptr->QueuedInput++; ReadDataAst (ioptr); } } return (status); } /*****************************************************************************/ /* */ static void ReadDataAst (struct RawLibIoStruct *ioptr) { int astatus, cnt, status, DataCount, DataSize; char *DataPtr; struct RawLibStruct *rawptr; /*********/ /* begin */ /*********/ rawptr = ioptr->RawLibPtr; WATCH_RAWLIB (rawptr, FI_LI, "READ AST %X!8XL !UL bytes", ioptr->IOsb.iosb$w_status, ioptr->IOsb.iosb$w_bcnt); if (rawptr->QueuedInput) rawptr->QueuedInput--; else RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); if (rawptr->WatchDogReadSecs) rawptr->WatchDogReadTime = CurrentTime + rawptr->WatchDogReadSecs; if (rawptr->WatchDogIdleSecs) rawptr->WatchDogIdleTime = CurrentTime + rawptr->WatchDogIdleSecs; /* these remain current for the duration of any AST */ rawptr->ReadDataPtr = ioptr->DataPtr; rawptr->ReadDataSize = ioptr->DataSize; rawptr->ReadDataDscPtr = ioptr->DataDscPtr; rawptr->ReadResultDscPtr = ioptr->ResultDscPtr; if (VMSok (rawptr->ReadStatus = ioptr->IOsb.iosb$w_status)) { #ifdef __VAX /* close enough! */ rawptr->InputMsgCount[0]++; #else *(__int64*)rawptr->InputMsgCount = *(__int64*)rawptr->InputMsgCount + 1; #endif } rawptr->ReadDataCount = ioptr->IOsb.iosb$w_bcnt; /* update result length if originally provided, otherwise data length */ if (rawptr->ReadResultDscPtr) rawptr->ReadResultDscPtr->dsc$w_length = ioptr->IOsb.iosb$w_bcnt; else rawptr->ReadDataDscPtr->dsc$w_length = ioptr->IOsb.iosb$w_bcnt; if (ioptr->AstFunction) ((void(*)())ioptr->AstFunction)(rawptr); /* reset after any AST has been delivered */ rawptr->ReadDataPtr = NULL; rawptr->ReadDataCount = rawptr->ReadDataSize = 0; rawptr->ReadDataDscPtr = rawptr->ReadResultDscPtr = NULL; rawptr->WatchDogReadTime = 0; if (rawptr->WatchDogIdleSecs) rawptr->WatchDogIdleTime = CurrentTime + rawptr->WatchDogIdleSecs; if (rawptr->WatchDogWakeSecs) rawptr->WatchDogWakeTime = CurrentTime + rawptr->WatchDogWakeSecs; if (rawptr->RawSocketShut) Shut (rawptr); free (ioptr); } /*****************************************************************************/ /* Return the (most recent) read status value. */ int RawLibReadStatus (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->ReadStatus); } /*****************************************************************************/ /* Return the (most recent) read buffer size (longword). */ int RawLibReadSize (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->ReadDataSize); } /*****************************************************************************/ /* Return the (most recent) read count value (longword). */ int RawLibReadCount (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->ReadDataCount); } /*****************************************************************************/ /* Return a pointer to the (most recent) read buffer. */ char* RawLibReadData (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->ReadDataPtr); } /*****************************************************************************/ /* Return a pointer to the (most recent) read data descriptor. */ struct dsc$descriptor_s* RawLibReadDataDsc (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (&rawptr->ReadDataDsc); } /*****************************************************************************/ /* Return a pointer to the total read count value (quadword). */ ulong* RawLibReadTotal (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return ((ulong*)&rawptr->InputCount); } /*****************************************************************************/ /* Return a pointer to the total messages read value (quadword). */ ulong* RawLibReadMsgTotal (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return ((ulong*)&rawptr->InputMsgCount); } /*****************************************************************************/ /* Write the data pointed to by the supplied string descriptor. */ int RawLibWriteDsc ( struct RawLibStruct *rawptr, struct dsc$descriptor_s *DataDsc, void *AstFunction ) { int status; /*********/ /* begin */ /*********/ if (DataDsc->dsc$b_class != DSC$K_CLASS_S && DataDsc->dsc$b_dtype != DSC$K_DTYPE_T) return (LIB$_INVSTRDES); rawptr->WriteDataDscPtr = DataDsc; status = RawLibWrite (rawptr, DataDsc->dsc$a_pointer, DataDsc->dsc$w_length, AstFunction); return (status); } /*****************************************************************************/ /* Queue a write to the client RAWSOCKET_OUTPUT mailbox. If an AST function is supplied then the write is asynchronous, otherwise blocking. */ int RawLibWrite ( struct RawLibStruct *rawptr, char *DataPtr, int DataCount, void *AstFunction ) { int astatus, status; uchar *cptr, *czptr, *sptr; struct RawLibIoStruct *ioptr; /*********/ /* begin */ /*********/ WATCH_RAWLIB (rawptr, FI_LI, "WRITE !UL bytes", DataCount); if (rawptr->RawSocketClosed) { MsgCallback (rawptr, __LINE__, SS$_SHUT, "can't write; closed"); rawptr->WriteStatus = SS$_SHUT; if (AstFunction && AstFunction != RAWLIB_ASYNCH) ((void(*)())AstFunction)(rawptr); return (SS$_SHUT); } ioptr = calloc (1, sizeof(struct RawLibIoStruct)); ioptr->RawLibPtr = rawptr; ioptr->DataPtr = DataPtr; ioptr->DataCount = DataCount; ioptr->DataDscPtr = rawptr->WriteDataDscPtr; if (ioptr->AstFunction = AstFunction) { /* asynchronous */ astatus = sys$setast (0); status = sys$qio (EfnNoWait, rawptr->OutputChannel, IO$_WRITELBLK | IO$M_READERCHECK, &ioptr->IOsb, WriteDataAst, ioptr, DataPtr, DataCount, 0, 0, 0, 0); if (VMSok(status)) rawptr->QueuedOutput++; if (astatus == SS$_WASSET) sys$setast (1); } else { /* synchronous */ status = sys$qiow (EfnWait, rawptr->OutputChannel, IO$_WRITELBLK | IO$M_READERCHECK, &ioptr->IOsb, 0, 0, DataPtr, DataCount, 0, 0, 0, 0); if (VMSok(status)) status = ioptr->IOsb.iosb$w_status; rawptr->QueuedOutput++; WriteDataAst (ioptr); } return (status); } /*****************************************************************************/ /* Write the message to the RawSocket. Message may be automatically fragmented. Can AST back to this function one or more times with asycnchronous I/O. */ static void WriteDataAst (struct RawLibIoStruct *ioptr) { int astatus, cnt, count, hcnt, length, status, DataCount; char *DataPtr; struct RawLibStruct *rawptr; /*********/ /* begin */ /*********/ rawptr = ioptr->RawLibPtr; WATCH_RAWLIB (rawptr, FI_LI, "WRITE AST %X!8XL", ioptr->IOsb.iosb$w_status); if (rawptr->QueuedOutput) rawptr->QueuedOutput--; else RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); if (rawptr->WatchDogIdleSecs) rawptr->WatchDogIdleTime = CurrentTime + rawptr->WatchDogIdleSecs; /* these remain current for the duration of any AST */ rawptr->WriteDataPtr = ioptr->DataPtr; rawptr->WriteDataCount = ioptr->DataCount; rawptr->WriteDataDscPtr = ioptr->DataDscPtr; if (VMSok (rawptr->WriteStatus = ioptr->IOsb.iosb$w_status)) { #ifdef __VAX /* close enough! */ rawptr->OutputMsgCount[0]++; #else *(__int64*)rawptr->OutputMsgCount = *(__int64*)rawptr->OutputMsgCount + 1; #endif } if (rawptr->WatchDogIdleSecs) rawptr->WatchDogIdleTime = CurrentTime + rawptr->WatchDogIdleSecs; if (rawptr->WatchDogWakeSecs) rawptr->WatchDogWakeTime = CurrentTime + rawptr->WatchDogWakeSecs; if (ioptr->AstFunction && ioptr->AstFunction != RAWLIB_ASYNCH) ((void(*)())ioptr->AstFunction)(rawptr); free (ioptr); } /*****************************************************************************/ /* Return the (most recent) write status value. */ int RawLibWriteStatus (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->WriteStatus); } /*****************************************************************************/ /* Return the (most recent) write count value (longword). */ int RawLibWriteCount (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->WriteDataCount); } /*****************************************************************************/ /* Return a pointer to the (most recent) write data descriptor. */ struct dsc$descriptor_s* RawLibWriteDataDsc (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (&rawptr->WriteDataDsc); } /*****************************************************************************/ /* Return a pointer to the total write count value (quadword). */ ulong* RawLibWriteTotal (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return ((ulong*)&rawptr->OutputCount); } /*****************************************************************************/ /* Return a pointer to the total write message value (quadword). */ ulong* RawLibWriteMsgTotal (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return ((ulong*)&rawptr->OutputMsgCount); } /*****************************************************************************/ /* Set the pointer to the user data associated with the RawSocket structure. */ void RawLibSetUserData ( struct RawLibStruct *rawptr, void *UserDataPtr ) { /*********/ /* begin */ /*********/ rawptr->UserDataPtr = UserDataPtr; } /*****************************************************************************/ /* Return the pointer to the user data. */ void* RawLibGetUserData (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->UserDataPtr); } /*****************************************************************************/ /* Set/reset the callout response AST. Returns the previous callout pointer. */ void* RawLibSetCallout ( struct RawLibStruct *rawptr, void *AstFunction ) { void *PrevCallout; /*********/ /* begin */ /*********/ PrevCallout = rawptr->CalloutAstFunction; rawptr->CalloutAstFunction = AstFunction; return (PrevCallout); } /*****************************************************************************/ /* Set number of seconds before the application is considered idle and exited. */ void RawLibSetLifeSecs (int LifeSecs) { /*********/ /* begin */ /*********/ if (!(WatchDogLifeSecs = LifeSecs)) WatchDogLifeSecs = DEFAULT_WATCHDOG_IDLE_SECS; } /*****************************************************************************/ /* Set number of seconds before a RawSocket is considered idle and closed. If a RawSocket is not specified then set global value. */ void RawLibSetIdleSecs ( struct RawLibStruct *rawptr, int IdleSecs ) { /*********/ /* begin */ /*********/ if (rawptr) { /* set RawSocket values */ if (!(rawptr->WatchDogIdleSecs = IdleSecs)) rawptr->WatchDogIdleSecs = WatchDogIdleSecs; rawptr->WatchDogIdleTime = CurrentTime + rawptr->WatchDogIdleSecs; } else { /* set global values */ if (!(WatchDogIdleSecs = IdleSecs)) WatchDogIdleSecs = DEFAULT_WATCHDOG_IDLE_SECS; } } /*****************************************************************************/ /* Set number of seconds a RawSocket read will wait on the client. If a RawSocket is not specified then set global value. */ void RawLibSetReadSecs ( struct RawLibStruct *rawptr, int ReadSecs ) { /*********/ /* begin */ /*********/ if (rawptr) { /* set RawSocket values */ if (!(rawptr->WatchDogReadSecs = ReadSecs)) rawptr->WatchDogReadSecs = WatchDogReadSecs; } else { /* set global values */ if (!(WatchDogReadSecs = ReadSecs)) WatchDogReadSecs = DEFAULT_WATCHDOG_IDLE_SECS; } } /*****************************************************************************/ /* Set/reset the wake callback function to the specified number of seconds (defaults to the global setting). Returns the previous callback pointer. */ void* RawLibSetWakeCallback ( struct RawLibStruct *rawptr, void *AstFunction, int WakeSecs ) { void *PrevCallback; /*********/ /* begin */ /*********/ if (rawptr) { /* set RawSocket values */ if (!(rawptr->WatchDogWakeSecs = WakeSecs)) rawptr->WatchDogWakeSecs = WatchDogWakeSecs; rawptr->WatchDogWakeTime = CurrentTime + rawptr->WatchDogWakeSecs; PrevCallback = rawptr->WakeCallbackFunction; rawptr->WakeCallbackFunction = AstFunction; } else { /* set global values */ if (!(WatchDogWakeSecs = WakeSecs)) WatchDogWakeSecs = DEFAULT_WATCHDOG_WAKE_SECS; WatchDogWakeTime = CurrentTime + WatchDogWakeSecs; PrevCallback = WakeCallbackFunction; WakeCallbackFunction = AstFunction; } return (PrevCallback); } /*****************************************************************************/ /* Set/reset the error callback function. Returns the previous callback pointer. */ void* RawLibSetMsgCallback ( struct RawLibStruct *rawptr, void *AstFunction ) { void *PrevCallback; /*********/ /* begin */ /*********/ PrevCallback = rawptr->MsgCallbackFunction; rawptr->MsgCallbackFunction = AstFunction; return (PrevCallback); } /*****************************************************************************/ /* Set wsLIB message data. The 'FormatString' must be a $FAO compiliant null-terminated string, with following parametersif required. Activate any set message callback. */ static void MsgCallback ( struct RawLibStruct *rawptr, int LineNumber, int VmsStatus, char *FormatString, ... ) { static $DESCRIPTOR (FormatFaoDsc, ""); int argcnt, cnt, status; ushort slen = 0; ulong *vecptr; ulong FaoVector [32]; char *cptr, *sptr, *zptr; char FormatBuffer [128]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); /* bit of a sanity check */ if (argcnt-4 > 32) RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); cnt = 0; /* some VERY basic sanity checking */ for (cptr = FormatString; *cptr; cptr++) { if (*cptr != '!') continue; cptr++; if (*cptr == '!') continue; cnt++; if (*cptr == '#') cnt++; if (*(USHORTPTR)cptr == '%T') cnt++; if (*(USHORTPTR)cptr == '%D') cnt++; } if (argcnt-4 != cnt) RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); zptr = (sptr = FormatBuffer) + sizeof(FormatBuffer)-1; for (cptr = "%X!8XL "; *cptr; *sptr++ = *cptr++); for (cptr = FormatString; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; FormatFaoDsc.dsc$a_pointer = FormatBuffer; FormatFaoDsc.dsc$w_length = sptr - FormatBuffer; vecptr = FaoVector; *vecptr++ = VmsStatus; va_start (argptr, FormatString); for (argcnt -= 4; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); rawptr->MsgDsc.dsc$b_class = DSC$K_CLASS_S; rawptr->MsgDsc.dsc$b_dtype = DSC$K_DTYPE_T; for (;;) { if (rawptr->MsgStringSize) { rawptr->MsgDsc.dsc$a_pointer = rawptr->MsgStringPtr; rawptr->MsgDsc.dsc$w_length = rawptr->MsgStringSize; status = sys$faol (&FormatFaoDsc, &slen, &rawptr->MsgDsc, &FaoVector); if (VMSnok (status)) RawLibExit (NULL, FI_LI, status); if (status != SS$_BUFFEROVF) break; } if (rawptr->MsgStringSize) free (rawptr->MsgStringPtr); rawptr->MsgStringSize += 127; rawptr->MsgStringPtr = calloc (1, rawptr->MsgStringSize+1); if (!rawptr->MsgStringPtr) RawLibExit (NULL, FI_LI, vaxc$errno); } rawptr->MsgStringPtr[rawptr->MsgStringLength=slen] = '\0'; rawptr->MsgDsc.dsc$w_length = slen; rawptr->MsgLineNumber = LineNumber; sys$gettim (&rawptr->MsgBinTime); if (rawptr->MsgCallbackFunction) (*rawptr->MsgCallbackFunction)(rawptr); } /****************************************************************************/ /* Return a pointer to the string descriptor of the latest error string. */ struct dsc$descriptor_s* RawLibMsgDsc (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (&rawptr->MsgDsc); } /****************************************************************************/ /* Return a pointer to the latest error string. */ char* RawLibMsgString (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->MsgStringPtr); } /****************************************************************************/ /* Return the wsLIB source code line at which the last error occured. */ int RawLibMsgLineNumber (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ return (rawptr->MsgLineNumber); } /*****************************************************************************/ /* Return the current wsLIB time in seconds (i.e. C-RTL, Unix time). */ unsigned int RawLibTime () { /*********/ /* begin */ /*********/ return (CurrentTime); } /*****************************************************************************/ /* Called every second to maintain various events in the RawSocket and application life-cycle. */ static void WatchDog () { static int DebugWatchDog = 0; static ulong OneSecondDelta [2] = { -10000000, -1 }; static ulong ExitTime; int status, StringLength; char StringBuffer [256]; struct RawLibStruct *rawptr; /*********/ /* begin */ /*********/ sys$gettim (&CurrentBinTime); CurrentTime = decc$fix_time (&CurrentBinTime); if (DebugWatchDog && ListHead) RawLibWatchScript (ListHead, FI_LI, "WATCHDOG !UL", CurrentTime); if (ListHead) ExitTime = 0; else if (!ExitTime) ExitTime = CurrentTime + WatchDogLifeSecs; else if (ExitTime < CurrentTime) exit (SS$_NORMAL); if (WatchDogWakeTime && WatchDogWakeTime < CurrentTime) { /* wake globally if requested */ WatchDogWakeTime = CurrentTime + WatchDogWakeSecs - 1; if (WakeCallbackFunction) sys$dclast (WakeCallbackFunction, 0, 0, 0); } for (rawptr = ListHead; rawptr; rawptr = rawptr->NextPtr) { if (DebugWatchDog) RawLibWatchScript (ListHead, FI_LI, "WATCHDOG 0x!8XL !UL !UL !UL", rawptr, rawptr->WatchDogIdleTime, rawptr->WatchDogReadTime, rawptr->WatchDogWakeTime); /* flush any watch log to disk every second */ if (rawptr->WatchLog) fsync (fileno(rawptr->WatchLog)); if (rawptr->WatchDogReadTime && rawptr->WatchDogReadTime < CurrentTime) { if (rawptr->WatchScript) WATCH_RAWLIB (rawptr, FI_LI, "READ expired 0x!8XL", rawptr); RawLibClose (rawptr); } else if (rawptr->WatchDogIdleTime && rawptr->WatchDogIdleTime < CurrentTime) { if (rawptr->WatchScript) WATCH_RAWLIB (rawptr, FI_LI, "IDLE expired 0x!8XL", rawptr); RawLibClose (rawptr); } else if (rawptr->WatchDogWakeTime && rawptr->WatchDogWakeTime < CurrentTime) { /* wake if requested */ rawptr->WatchDogWakeTime = CurrentTime + rawptr->WatchDogWakeSecs - 1; if (rawptr->WakeCallbackFunction) { if (rawptr->WatchScript) WATCH_RAWLIB (rawptr, FI_LI, "WAKE expired 0x!8XL", rawptr); sys$dclast (rawptr->WakeCallbackFunction, rawptr, 0, 0); } } } status = sys$setimr (0, &OneSecondDelta, WatchDog, 0, 0); if (VMSnok(status)) RawLibExit (NULL, FI_LI, status); } /*****************************************************************************/ /* Exit from the current image reporting to the server RAWSOCKET_OUTPUT stream (if a channel assigned) or (last-ditch) to the exit code module name and line number. */ void RawLibExit ( struct RawLibStruct *rawptr, char *SourceModuleName, int SourceLineNumber, int status ) { static ulong OneSecondDelta [2] = { -10000000, -1 }; static $DESCRIPTOR (FaoDsc, "BYE-BYE [!AZ:!UL] %X!8XL\n\0"); short slen; char MsgBuffer [256]; $DESCRIPTOR (MsgDsc, MsgBuffer); /*********/ /* begin */ /*********/ sys$fao (&FaoDsc, &slen, &MsgDsc, SourceModuleName, SourceLineNumber, status); if (rawptr && rawptr->OutputChannel) sys$qiow (EfnWait, rawptr->OutputChannel, IO$_WRITELBLK | IO$M_READERCHECK, 0, 0, 0, MsgBuffer, slen-1, 0, 0, 0, 0); else { fputs (MsgBuffer, stdout); fflush (stdout); } sys$schdwk (0, 0, OneSecondDelta, 0); sys$hiber (); sys$delprc (0, 0, 0); } /*****************************************************************************/ /* The 'FormatString' must be a $FAO compliant null-terminated string, with following parameters. */ void RawLibCallout ( struct RawLibStruct *rawptr, char *FormatString, ... ) { static $DESCRIPTOR (ErrorFaoDsc, "!!WATCH: $FAO %X!8XL"); int argcnt, astatus, cnt, status; ushort slen = 0; ulong *vecptr; ulong FaoVector [32]; char *aptr, *cptr; char CalloutBuffer [1024]; va_list argptr; $DESCRIPTOR (CalloutBufferDsc, CalloutBuffer); $DESCRIPTOR (FormatDsc, ""); /*********/ /* begin */ /*********/ if (!CgiPlusEscLength && !CgiPlusEotLength) return; if (!rawptr->OutputChannel) return; va_count (argcnt); /* can't call callout while in a callout response delivery */ if (rawptr->CalloutInProgress) RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); /* bit of a sanity check */ if (argcnt-2 > 32) RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); cnt = 0; for (cptr = FormatString; *cptr; cptr++) if (*cptr == '!' && *(USHORTPTR)cptr != '!!') cnt++; if (argcnt-2 != cnt) RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); FormatDsc.dsc$a_pointer = FormatString; FormatDsc.dsc$w_length = strlen(FormatString); vecptr = FaoVector; va_start (argptr, FormatString); for (argcnt -= 2; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); status = sys$faol (&FormatDsc, &slen, &CalloutBufferDsc, &FaoVector); if (VMSnok(status)) status = sys$fao (&ErrorFaoDsc, &slen, &CalloutBufferDsc, status); /* allocate a pointer plus a buffer (freed by WriteFreeAst()) */ aptr = calloc (1, sizeof(struct RawLibStruct*) + slen); if (!aptr) RawLibExit (rawptr, FI_LI, vaxc$errno); *(struct RawLibStruct**)aptr = rawptr; cptr = aptr + sizeof(struct RawLibStruct*); memcpy (cptr, CalloutBuffer, slen); astatus = sys$setast (0); status = sys$qio (EfnNoWait, rawptr->OutputChannel, IO$_WRITELBLK | IO$M_READERCHECK, 0, WriteOutputAst, rawptr, CgiPlusEscPtr, CgiPlusEscLength, 0, 0, 0, 0); if (VMSok(status)) { rawptr->QueuedOutput++; status = sys$qio (EfnNoWait, rawptr->OutputChannel, IO$_WRITELBLK | IO$M_READERCHECK, 0, WriteFreeAst, aptr, cptr, slen, 0, 0, 0, 0); if (VMSok(status)) { rawptr->QueuedOutput++; status = sys$qio (EfnNoWait, rawptr->OutputChannel, IO$_WRITELBLK | IO$M_READERCHECK, 0, WriteOutputAst, rawptr, CgiPlusEotPtr, CgiPlusEotLength, 0, 0, 0, 0); if (VMSok(status)) rawptr->QueuedOutput++; } } if (astatus == SS$_WASSET) sys$setast (1); } /*****************************************************************************/ /* Send a script "WATCH:" callout to the server (if [x]script enabled). The 'SourceModuleName' can be NULL. The 'FormatString' must be a $FAO compiliant null-terminated string, with following parameters. A specific channel is assigned for WATCH output so that it can be deassigned as late in request processing as possible. */ void RawLibWatchScript ( struct RawLibStruct *rawptr, char *SourceModuleName, int SourceLineNumber, char *FormatString, ... ) { static $DESCRIPTOR (ErrorFaoDsc, "!!WATCH: $FAO %X!8XL"); static $DESCRIPTOR (TimeFaoDsc, "!%T\0"); static $DESCRIPTOR (Watch1FaoDsc, "!!!!WATCH: [!AZ:!4ZL] !AZ"); static $DESCRIPTOR (Watch2FaoDsc, "!!!!WATCH: !AZ"); static struct RawLibStruct FakeIt; int argcnt, astatus, cnt, status; ushort slen = 0; ulong *vecptr; ulong FaoVector [32]; char *aptr, *cptr; char TimeBuffer [32], WatchBuffer [1024], WatchFao [256]; va_list argptr; $DESCRIPTOR (FaoDsc, WatchFao); $DESCRIPTOR (TimeBufferDsc, TimeBuffer); $DESCRIPTOR (WatchBufferDsc, WatchBuffer); /*********/ /* begin */ /*********/ if (!rawptr) { rawptr = &FakeIt; if (!rawptr->WatchScript) { rawptr->WatchScript = 1; if ((cptr = getenv ("WASD_RAWLIB_WATCH_LOG")) != NULL) if ((rawptr->WatchLog = fopen (cptr, "w", "shr=get")) != NULL) RawLibExit (NULL, FI_LI, vaxc$errno); } if (!rawptr->WatchLog) return; } else { if (!rawptr->WatchScript) return; if (!rawptr->OutputChannel) return; /* can't call callout while in a callout response delivery */ if (rawptr->CalloutInProgress) RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); } va_count (argcnt); /* bit of a sanity check */ if (argcnt-4 > 32) RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); cnt = 0; for (cptr = FormatString; *cptr; cptr++) { if (*cptr != '!') continue; cptr++; if (*cptr == '!') continue; cnt++; if (*cptr == '#') cnt++; if (*(USHORTPTR)cptr == '%T') cnt++; if (*(USHORTPTR)cptr == '%D') cnt++; } if (argcnt-4 != cnt) RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); if (SourceModuleName) sys$fao (&Watch1FaoDsc, &slen, &FaoDsc, SourceModuleName, SourceLineNumber, FormatString); else sys$fao (&Watch2FaoDsc, &slen, &FaoDsc, FormatString); FaoDsc.dsc$w_length = slen; vecptr = FaoVector; va_start (argptr, FormatString); for (argcnt -= 4; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); status = sys$faol (&FaoDsc, &slen, &WatchBufferDsc, &FaoVector); if (!(status & 1)) status = sys$fao (&ErrorFaoDsc, &slen, &WatchBufferDsc, status); if (!CgiPlusEscLength && !CgiPlusEotLength) { fprintf (stdout, "%*.*s\n", slen, slen, WatchBuffer); return; } if (rawptr->WatchLog) { sys$fao (&TimeFaoDsc, 0, &TimeBufferDsc, 0); fprintf (rawptr->WatchLog, "%s %*.*s\n", TimeBuffer, slen-8, slen-8, WatchBuffer+8); return; } /* allocate a pointer plus a buffer (freed by WriteFreeAst()) */ aptr = calloc (1, sizeof(struct RawLibStruct*) + slen); if (!aptr) RawLibExit (rawptr, FI_LI, vaxc$errno); *(struct RawLibStruct**)aptr = rawptr; cptr = aptr + sizeof(struct RawLibStruct*); memcpy (cptr, WatchBuffer, slen); astatus = sys$setast (0); status = sys$qio (EfnNoWait, rawptr->OutputChannel, IO$_WRITELBLK | IO$M_READERCHECK, 0, WriteOutputAst, rawptr, CgiPlusEscPtr, CgiPlusEscLength, 0, 0, 0, 0); if (VMSok(status)) { rawptr->QueuedOutput++; status = sys$qio (EfnNoWait, rawptr->OutputChannel, IO$_WRITELBLK | IO$M_READERCHECK, 0, WriteFreeAst, aptr, cptr, slen, 0, 0, 0, 0); if (VMSok(status)) { rawptr->QueuedOutput++; status = sys$qio (EfnNoWait, rawptr->OutputChannel, IO$_WRITELBLK | IO$M_READERCHECK, 0, WriteOutputAst, rawptr, CgiPlusEotPtr, CgiPlusEotLength, 0, 0, 0, 0); if (VMSok(status)) rawptr->QueuedOutput++; } } if (VMSnok (status)) RawLibExit (rawptr, FI_LI, status); if (astatus == SS$_WASSET) sys$setast (1); } /*****************************************************************************/ /* Just decrement the queued output counter. */ void WriteOutputAst (struct RawLibStruct *rawptr) { /*********/ /* begin */ /*********/ if (rawptr->QueuedOutput) rawptr->QueuedOutput--; else RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); if (rawptr->RawSocketShut) Shut (rawptr); } /*****************************************************************************/ /* The parameter is an address to allocated memory. The memory contains a pointer to a RawLib structure and after that opaque data (actually just a write buffer but what the hey). Get the pointer to the owning RawLib structure. Free the allocated memory. Decrement the RawLib structure's queued output counter. */ void WriteFreeAst (char *aptr) { struct RawLibStruct *rawptr; /*********/ /* begin */ /*********/ rawptr = *(struct RawLibStruct**)aptr; free (aptr); if (rawptr->QueuedOutput) rawptr->QueuedOutput--; else RawLibExit (rawptr, FI_LI, SS$_BUGCHECK); if (rawptr->RawSocketShut) Shut (rawptr); } /*****************************************************************************/ /* If a CGIplus environment then output the end-of-request sentinal. */ void RawLibCgiPlusEof () { /*********/ /* begin */ /*********/ if (!CgiPlusEofLength) return; fflush (stdout); fputs (CgiPlusEofPtr, stdout); fflush (stdout); } /*****************************************************************************/ /* Output the callout end sentinal. */ void RawLibCgiPlusEot () { /*********/ /* begin */ /*********/ if (!CgiPlusEotLength) return; fflush (stdout); fputs (CgiPlusEotPtr, stdout); fflush (stdout); } /*****************************************************************************/ /* Output the callout begin sentinal. */ void RawLibCgiPlusEsc () { /*********/ /* begin */ /*********/ if (!CgiPlusEscLength) return; fflush (stdout); fputs (CgiPlusEscPtr, stdout); fflush (stdout); } /*****************************************************************************/ /* Output the callout begin sentinal. */ void RawLibCalloutStart () { /*********/ /* begin */ /*********/ if (!CgiPlusEscLength) return; fflush (stdout); fputs (CgiPlusEscPtr, stdout); fflush (stdout); } /*****************************************************************************/ /* Return true if it's a CGIplus execution environment. */ int RawLibIsCgiPlus () { static int InitIs; /*********/ /* begin */ /*********/ if (!InitIs) { InitIs = 1; if (CgiPlusEofPtr = getenv("CGIPLUSEOF")) CgiPlusEofLength = strlen(CgiPlusEofPtr); if (CgiPlusEscPtr = getenv("CGIPLUSESC")) CgiPlusEscLength = strlen(CgiPlusEscPtr); if (CgiPlusEotPtr = getenv("CGIPLUSEOT")) CgiPlusEotLength = strlen(CgiPlusEotPtr); } return (CgiPlusEofLength); } /*****************************************************************************/ /* Are CGI variables currently available? Returns true or false. */ int RawLibCgiVarAvailable () { /*********/ /* begin */ /*********/ return (CgiVariablesAvailable); } /*****************************************************************************/ /* String descriptor equivalent of RawLibCgiVar() with the same requirements and constraints. Set 'ValueDsc' to the value of the CGI variable (or empty if it doesn't exist). Return the length of the value of -1 if the CGI variable name does not exist. */ int RawLibCgiVarDsc ( struct dsc$descriptor_s *NameDsc, struct dsc$descriptor_s *ValueDsc ) { char *cptr, *czptr, *sptr, *zptr; char VarName [256]; /*********/ /* begin */ /*********/ if (NameDsc->dsc$b_class != DSC$K_CLASS_S && ValueDsc->dsc$b_dtype != DSC$K_DTYPE_T) return (LIB$_INVSTRDES); if (ValueDsc->dsc$b_class != DSC$K_CLASS_S && ValueDsc->dsc$b_dtype != DSC$K_DTYPE_T) return (LIB$_INVSTRDES); zptr = (sptr = VarName) + sizeof(VarName)-1; czptr = (cptr = NameDsc->dsc$a_pointer) + NameDsc->dsc$w_length; while (cptr < czptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (cptr = RawLibCgiVarNull (VarName)) { for (sptr = cptr; *sptr; sptr++); ValueDsc->dsc$w_length = sptr - cptr; ValueDsc->dsc$a_pointer = cptr; return (SS$_NORMAL); } else return (SS$_ITEMNOTFOUND); } /*****************************************************************************/ /* Return empty string rather than NULL if the CGI variable does not exist. */ char* RawLibCgiVar (char* VarName) { char *cptr; /*********/ /* begin */ /*********/ if (cptr = RawLibCgiVarNull (VarName)) return (cptr); else return (""); } /*****************************************************************************/ /* Return the value of a CGI variable regardless of whether it is used in a standard CGI environment or a WASD CGIplus environment. Automatically switches WASD V7.2 into 'struct' mode for significantly improved performance. Call with 'VarName' empty ("") to synchronize CGIplus requests. This waits for a CGIplus variable stream, checks if it's in 'record' or 'struct' mode, reads the stream appropriately before returning ready to provide variables. DO NOT modify the character string returned by this function. Copy to other storage if this is necessary. The behaviour is indeterminate if the returned values are modified in any way! All CGIplus variables may be returned by making successive calls using a 'VarName' of "*" (often useful when debugging). NULL is returned when variables exhausted. */ char* RawLibCgiVarNull (char *VarName) { #define SOUS sizeof(ushort) static FILE *CgiPlusIn; int Length; char WwwVarName [256]; char *bptr, *cptr, *sptr; /*********/ /* begin */ /*********/ /* if not using the AST-driven RawLibOnNextRequest() */ if (!CgiPlusInChannel) { if (!StructBufferSize) { /**************/ /* initialise */ /**************/ if (RawLibIsCgiPlus()) { if ((cptr = getenv ("WASD_RAWLIB_STRUCT_BUFFER_SIZE")) != NULL) StructBufferSize = atoi(cptr); if (StructBufferSize < RAWLIB_STRUCT_BUFFER_SIZE) StructBufferSize = RAWLIB_STRUCT_BUFFER_SIZE; StructBufferPtr = calloc (1, StructBufferSize); if (!StructBufferPtr) RawLibExit (NULL, FI_LI, vaxc$errno); StructBufferLength = 0; } else StructBufferSize = -1; } if (VarName == NULL || !VarName[0]) { /* initialize */ StructBufferLength = WwwPrefix = 0; NextVarNamePtr = StructBufferPtr; if (CgiPlusEofPtr == NULL) WwwPrefix = (getenv ("WWW_SERVER_SOFTWARE") != NULL); if (VarName == NULL) return (NULL); } } if (VarName[0]) { /***************************/ /* return a variable value */ /***************************/ if (*(ULONGPTR)VarName == 'WWW_' && !WwwPrefix) VarName += 4; else if (*(ULONGPTR)VarName != 'WWW_' && WwwPrefix) { strcpy (WwwVarName, "WWW_"); strcpy (WwwVarName+4, VarName); VarName = WwwVarName; } if (CgiPlusEofPtr == NULL) { /* standard CGI environment */ CgiVariablesAvailable = 1; return (getenv (VarName)); } if (!CgiVariablesAvailable) return (NULL); if (VarName[0] == '*') { /* return each CGIplus variable in successive calls */ if (!(Length = *(USHORTPTR)NextVarNamePtr)) { NextVarNamePtr = StructBufferPtr; return (NULL); } sptr = (NextVarNamePtr += SOUS); NextVarNamePtr += Length; return (sptr); } /* return a pointer to this CGIplus variable's value */ for (bptr = StructBufferPtr; Length = *(USHORTPTR)bptr; bptr += Length) { sptr = (bptr += SOUS); for (cptr = VarName; *cptr && *sptr && *sptr != '='; cptr++, sptr++) if (toupper(*cptr) != toupper(*sptr)) break; /* if found return a pointer to the value */ if (!*cptr && *sptr == '=') return (sptr+1); } /* not found */ return (NULL); } /*****************************/ /* get the CGIplus variables */ /*****************************/ if (RequestSynchronisation == NULL) RequestSynchronisation = (void*)RawLibCgiVarNull; else if (RequestSynchronisation != (void*)RawLibCgiVarNull) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); /* cannot "sync" in a non-CGIplus environment */ if (CgiPlusEofPtr == NULL) return (NULL); /* the CGIPLUSIN stream can be left open */ if (CgiPlusIn == NULL) if ((CgiPlusIn = fopen (getenv("CGIPLUSIN"), "r")) == NULL) RawLibExit (NULL, FI_LI, vaxc$errno); CgiVariablesAvailable = WwwPrefix = 0; /* get the starting record (the essentially discardable one) */ for (;;) { cptr = fgets (StructBufferPtr, StructBufferSize, CgiPlusIn); if (cptr == NULL) RawLibExit (NULL, FI_LI, vaxc$errno); /* if the starting sentinal is detected then break */ if (*(USHORTPTR)cptr == '!\0' || *(USHORTPTR)cptr == '!\n' || (*(USHORTPTR)cptr == '!!' && isdigit(*(cptr+2)))) break; } if (*(USHORTPTR)cptr == '!!') { /********************/ /* CGIplus 'struct' */ /********************/ /* get the size of the binary structure */ StructBufferLength = atoi(cptr+2); if (StructBufferLength <= 0) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); if (StructBufferLength > StructBufferSize) { while (StructBufferLength > StructBufferSize) StructBufferSize *= 2; free (StructBufferPtr); StructBufferPtr = calloc (1, StructBufferSize); if (!StructBufferPtr) RawLibExit (NULL, FI_LI, vaxc$errno); NextVarNamePtr = StructBufferPtr; } if (!fread (StructBufferPtr, 1, StructBufferLength, CgiPlusIn)) RawLibExit (NULL, FI_LI, vaxc$errno); } else { /*********************/ /* CGIplus 'records' */ /*********************/ /* reconstructs the original 'struct'ure from the records */ sptr = (bptr = StructBufferPtr) + StructBufferSize; while (fgets (bptr+SOUS, sptr-(bptr+SOUS), CgiPlusIn) != NULL) { /* first empty record (line) terminates variables */ if (bptr[SOUS] == '\n') break; /* note the location of the length word */ cptr = bptr; for (bptr += SOUS; *bptr && *bptr != '\n'; bptr++); if (*bptr != '\n') RawLibExit (NULL, FI_LI, SS$_BUGCHECK); *bptr++ = '\0'; if (bptr >= sptr) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); /* update the length word */ *(USHORTPTR)cptr = bptr - (cptr + SOUS); } if (bptr >= sptr) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); /* terminate with a zero-length entry */ *(USHORTPTR)bptr = 0; StructBufferLength = (bptr + SOUS) - StructBufferPtr; } /***********************/ /* variables available */ /***********************/ CgiVariablesAvailable = 1; sptr = StructBufferPtr + SOUS; if (*(ULONGPTR)sptr == 'WWW_') WwwPrefix = 1; if (!CalloutDone) { /* provide the CGI callout to set CGIplus into 'struct' mode */ fflush (stdout); fputs (CgiPlusEscPtr, stdout); fflush (stdout); /* the leading '!' indicates we're not going to read the response */ fputs ("!CGIPLUS: struct", stdout); fflush (stdout); fputs (CgiPlusEotPtr, stdout); fflush (stdout); /* don't need to do this again (the '!!' tells us what mode) */ CalloutDone = 1; } return (NULL); # undef SOUS } /*****************************************************************************/ /* Asynchronous notification of the next request becoming available. This function should be called once during script initialisation specifying the address of the function to be notified of a new request. Until the script issues the CGIplus EOF sentinal the RawLibCgiVar..() family of functions can be used to retrieve the CGI variable values. This asynchronous and the synchronous RawLibCgiVar("") approaches to request synchronisation are mutually exclusive and once one is used, using the other results in a bugcheck. */ void RawLibOnNextRequest (void *AstFunction) { #define SOUS sizeof(ushort) static int CGIplusStruct = 0, StartSentinal = 0; static char SentinalBuffer [256]; static $DESCRIPTOR (CgiPlusInDsc, ""); static struct RawLibIOsb IOsb; static void *RequestAstFunction; int bcnt, status; char *bptr, *cptr; /*********/ /* begin */ /*********/ if (RequestSynchronisation == NULL) RequestSynchronisation = RawLibOnNextRequest; else if (RequestSynchronisation != RawLibOnNextRequest) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); if (!CgiPlusInChannel) { /**************/ /* initialise */ /**************/ /* ensure initialisation */ if (!RawLibIsCgiPlus()) { /* hmmm, standard CGI environment - do something sensible */ sys$dclast (AstFunction, 0, 0, 0); return; } if (!AstFunction) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); RequestAstFunction = AstFunction; if ((cptr = getenv("CGIPLUSIN")) == NULL) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); CgiPlusInDsc.dsc$a_pointer = cptr; CgiPlusInDsc.dsc$w_length = strlen(cptr); status = sys$assign (&CgiPlusInDsc, &CgiPlusInChannel, 0, 0); if (VMSnok(status)) RawLibExit (NULL, FI_LI, status); if ((cptr = getenv ("WASD_RAWLIB_STRUCT_BUFFER_SIZE")) != NULL) StructBufferSize = atoi(cptr); if (StructBufferSize < RAWLIB_STRUCT_BUFFER_SIZE) StructBufferSize = RAWLIB_STRUCT_BUFFER_SIZE; StructBufferPtr = calloc (1, StructBufferSize+2); if (!StructBufferPtr) RawLibExit (NULL, FI_LI, vaxc$errno); StructBufferLength = 0; status = sys$qio (EfnNoWait, CgiPlusInChannel, IO$_READLBLK, &IOsb, RawLibOnNextRequest, NULL, SentinalBuffer, sizeof(SentinalBuffer)-1, 0, 0, 0, 0); if (VMSnok(status)) RawLibExit (NULL, FI_LI, status); return; } else if (AstFunction) { /* modify AST function */ RequestAstFunction = AstFunction; return; } /***************/ /* read stream */ /***************/ if (!CgiPlusInChannel) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); if (AstFunction) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); if (IOsb.iosb$w_status && VMSnok(IOsb.iosb$w_status)) RawLibExit (NULL, FI_LI, IOsb.iosb$w_status); if (!StartSentinal) { cptr = SentinalBuffer; cptr[IOsb.iosb$w_bcnt] = '\0'; /* if the starting sentinal is not detected then ignore */ if (!(*(USHORTPTR)cptr == '!\0' || *(USHORTPTR)cptr == '!\n' || (*(USHORTPTR)cptr == '!!' && isdigit(*(cptr+2))))) { status = sys$qio (EfnNoWait, CgiPlusInChannel, IO$_READLBLK, &IOsb, RawLibOnNextRequest, NULL, SentinalBuffer, sizeof(SentinalBuffer)-1, 0, 0, 0, 0); if (VMSnok(status)) RawLibExit (NULL, FI_LI, status); return; } StartSentinal = 1; bcnt = CgiVariablesAvailable = StructBufferLength = 0; if (*(USHORTPTR)cptr == '!!' && isdigit(*(cptr+2))) { /* get the size of the binary structure */ StructBufferLength = atoi(cptr+2); if (StructBufferLength <= 0) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); if (StructBufferLength > StructBufferSize) { while (StructBufferLength > StructBufferSize) StructBufferSize *= 2; free (StructBufferPtr); StructBufferPtr = calloc (1, StructBufferSize+2); if (!StructBufferPtr) RawLibExit (NULL, FI_LI, vaxc$errno); NextVarNamePtr = StructBufferPtr; } CGIplusStruct = 1; /* drop through to the structure processing code */ } else { /* read the first variable record */ status = sys$qio (EfnNoWait, CgiPlusInChannel, IO$_READLBLK, &IOsb, RawLibOnNextRequest, NULL, StructBufferPtr + SOUS, StructBufferSize - SOUS, 0, 0, 0, 0); if (VMSnok(status)) RawLibExit (NULL, FI_LI, status); CGIplusStruct = 0; return; } } else if (CGIplusStruct) { cptr = StructBufferPtr + StructBufferLength; cptr[bcnt = IOsb.iosb$w_bcnt] = '\0'; } else { cptr = StructBufferPtr + StructBufferLength + SOUS; cptr[bcnt = IOsb.iosb$w_bcnt] = '\0'; } if (CGIplusStruct) { /********************/ /* CGIplus 'struct' */ /********************/ if (bcnt < StructBufferLength) { status = sys$qio (EfnNoWait, CgiPlusInChannel, IO$_READLBLK, &IOsb, RawLibOnNextRequest, NULL, StructBufferPtr, StructBufferLength, 0, 0, 0, 0); if (VMSnok(status)) RawLibExit (NULL, FI_LI, status); return; } if (bcnt > StructBufferLength) RawLibExit (NULL, FI_LI, SS$_BUGCHECK); } else { /*********************/ /* CGIplus 'records' */ /*********************/ /* first empty record terminates variables */ if (bcnt) { /* reconstructs the original 'struct'ure from the records */ bptr = StructBufferPtr + StructBufferLength; /* update the length word */ *(USHORTPTR)bptr = bcnt; /* terminate with a zero-length entry */ bptr += bcnt + SOUS; *(USHORTPTR)bptr = 0; StructBufferLength = bptr - StructBufferPtr; status = sys$qio (EfnNoWait, CgiPlusInChannel, IO$_READLBLK, &IOsb, RawLibOnNextRequest, NULL, StructBufferPtr + StructBufferLength + SOUS, StructBufferSize - StructBufferLength - SOUS, 0, 0, 0, 0); if (VMSnok(status)) RawLibExit (NULL, FI_LI, status); return; } } /***********************/ /* variables available */ /***********************/ bptr = StructBufferPtr + SOUS; if (*(ULONGPTR)bptr == 'WWW_') WwwPrefix = 1; if (!CalloutDone) { /* provide the CGI callout to set CGIplus into 'struct' mode */ fflush (stdout); fputs (CgiPlusEscPtr, stdout); fflush (stdout); /* the leading '!' indicates we're not going to read the response */ fputs ("!CGIPLUS: struct", stdout); fflush (stdout); fputs (CgiPlusEotPtr, stdout); fflush (stdout); /* don't need to do this again */ CalloutDone = 1; } CgiVariablesAvailable = 1; sys$dclast (RequestAstFunction, 0, 0, 0); /********************************/ /* queue ready for next request */ /********************************/ StartSentinal = 0; status = sys$qio (EfnNoWait, CgiPlusInChannel, IO$_READLBLK, &IOsb, RawLibOnNextRequest, NULL, SentinalBuffer, sizeof(SentinalBuffer)-1, 0, 0, 0, 0); if (VMSnok(status)) RawLibExit (NULL, FI_LI, status); # undef SOUS } /*****************************************************************************/