#module LAT_FORWARD_CONNECT "X1.0-001" /* **++ ** ** MODULE DESCRIPTION: ** ** In initiating and maintaining an outbound LAT session from the local ** node, this program demonstrates the following LAT $QIO functions: ** ** o Cloning the LAT template device (LTA0:) ** o IO$M_LT_SETMODE ** o IO$M_LT_CONNECT (on forward port) ** o IO$M_LT_SENSEMODE ** **-- */ /* ** ** INCLUDE FILES ** */ #include /* VMS Descriptor Definitions */ #include /* I/O Function Codes Definitions */ #include /* LAT Definitions */ #include /* System Service Return Status Code Definitions */ #include /* Terminal Characteristics */ #include /* Terminal Extended Characteristics */ /* ** ** MACRO DEFINITIONS ** */ /* ** Service name which the session will be to. */ #define SERVICE_NAME "LAT_SERVICE" #define SERVICE_NAME_LENGTH 11 /* ** For the sake of clarity, the sizes of the buffers used for reading from ** and writing to the LTA and TT devices are set to the values below. In ** order to gain maximum throughput from this program, the following SYSGEN ** parameters can be set: ** ** o TTY_ALTYPAHD - 1500 ** o TTY_TYPAHDSZ - 80 ** ** To get the best performance from this program without touching these ** SYSGEN parameters on your system, modify the program to set the size of ** the buffers to the following: ** ** o LTA_BUFFER_SIZE = MIN(TTY_ALTYPAHD, 1500) ** o TT_BUFFER_SIZE = MIN(TTY_TYPAHDSZ, 132) */ #define LTA_MAXBUF 1500 #define TT_MAXBUF 80 /* ** Size of the LAT SENSEmode itemlist. */ #define MAX_SENSE_ITEMLIST_SIZE 1500 /* ** Character user can press to terminate the LAT connection (CTRL+\). */ #define CONNECTION_TERMINATOR 0x1C /* ** ** FUNCTION PROTOTYPES ** */ unsigned long SetDeviceChars(void); void ConnectAST(void); void LTAreadChannelAST(void); void TTreadChannelAST(void); void LTAhangupHandler(void); void EndSession(void); void ExitHandler(void); /* ** ** GLOBAL DATA ** */ char *LTAbuffer, /* LTA device I/O buffer */ *TTbuffer, /* TT device I/O buffer */ /* ** Text for LAT reject codes. Note that some LAT ** implementations will return a 0 reject code to ** indicate a normal disconnect. */ *LATrejectTable[] = { "Unknown", "User requested disconnect", "System shutdown in progress", "Invalid slot received", "Invalid service class received", "Insufficient resources at server", "Port or service in use", "No such service", "Service is disabled", "Service is not offeredon the requested port", "Port name is unknown", "Invalid service password", "Remote entry is not in queue", "Immediate access rejected", "Access denied", "Corrupted request", "Requested function is not supported", "Session cannot be started", "Queue entry deleted by server", "Illegal request parameters" }; unsigned short LTAchannel, /* LTA device I/O channel */ TTchannel, /* TT device I/O channel */ LTA_QIOiosb[4], /* IOSB for LTA device functions */ TT_QIOiosb[4]; /* IOSB for TT device functions */ unsigned long ReadTerminatorMask[2] = { 0, 0 }, /* $QIO read terminator mask */ SavedTTdeviceChar[3], /* Saved TT device characteristics */ DeviceCharBuffSize = sizeof(SavedTTdeviceChar); /* Size of device characteristics buffer*/ ExitConditionValue, /* Exit condition value of program */ LATrejectTableSize =/* Number of elements in LAT reject tbl */ sizeof(LATrejectTable) / sizeof(LATrejectTable[0]); /* ** Itemlist for setting LAT port with the target service name. */ struct { unsigned short item_code; char item_byte_count; char item_value[ SERVICE_NAME_LENGTH ]; } PortSetmodeItemlist = { LAT$_ITM_TARGET_SERVICE_NAME, SERVICE_NAME_LENGTH, SERVICE_NAME }; /* ** Exit handler block. */ struct { unsigned long flink; void (*exit_handler)(); unsigned long arg_count; unsigned long *exit_status; } ExitHandlerBlock = { 0, ExitHandler, 1, &ExitConditionValue }; /* ** Devices which channels are assigned to. */ $DESCRIPTOR(LTAtemplateDSC, "LTA0:"); $DESCRIPTOR(TTchannelDSC, "SYS$COMMAND"); main() { /* ** Local Variables: */ unsigned long status, portSetmodeItemlistSize = sizeof(PortSetmodeItemlist); /* ** BEGIN: ** ** Declare an exit handler. */ if (!((status = sys$dclexh(&ExitHandlerBlock)) & 1)) lib$signal(status); /* ** Assign a channel to LTA0: to get a forward LAT port and assign a ** channel to the terminal. */ if (!((status = sys$assign(<AtemplateDSC, <Achannel, 0, 0)) & 1)) lib$signal(status); if (!((status = sys$assign(&TTchannelDSC, &TTchannel, 0, 0)) & 1)) lib$signal(status); /* ** Allocate memory for the channel data buffers. */ LTAbuffer = malloc(LTA_MAXBUF); TTbuffer = malloc(TT_MAXBUF); /* ** Set device characteristics for the two channels. */ if (!((status = SetDeviceChars()) & 1)) lib$signal(status); /* ** Do SETmode $QIO to set the port entity with the target service name ** specified in the item list. */ if (!((status = sys$qiow( 0, LTAchannel, IO$_TTY_PORT|IO$M_LT_SETMODE, <A_QIOiosb, 0, 0, &PortSetmodeItemlist, portSetmodeItemlistSize, LAT$C_ENT_PORT|(LAT$C_ENTS_OLD << 0x10), 0, 0, 0)) & 1)) lib$signal(status); if (!(LTA_QIOiosb[0] & 1)) lib$signal(LTA_QIOiosb[0]); /* ** Enable a CTRL+Y AST on the LAT channel. */ if (!((status = sys$qiow( 0, LTAchannel, IO$_SETMODE|IO$M_CTRLYAST, <A_QIOiosb, 0, 0, LTAhangupHandler, 0, 0, 0, 0, 0)) & 1)) lib$signal(status); if (!(LTA_QIOiosb[0] & 1)) lib$signal(LTA_QIOiosb[0]); /* ** Post the first read (with AST) on the LTA device to ensure that the ** first burst of data from the target service is not lost. It is very ** important that the first read is queued before doing the connect ** $QIO to ensure no data lossage. */ if (!((status = sys$qio( 0, LTAchannel, IO$_READVBLK|IO$M_NOECHO, <A_QIOiosb, LTAreadChannelAST, 0, LTAbuffer, 1, 0, &ReadTerminatorMask, 0, 0)) & 1)) lib$signal(status); /* ** Do the LAT connect $QIO and hibernate until program exit. The ** ConnectAST will execute when the connection completes and post the ** initial read on the TT channel. */ if (!((status = sys$qio( 0, LTAchannel, IO$_TTY_PORT|IO$M_LT_CONNECT, <A_QIOiosb, ConnectAST, 0, 0, 0, 0, 0, 0, 0)) & 1)) lib$signal(status); sys$hiber(); } /* END - main() */ /* **++ ** ** FUNCTIONAL DESCRIPTION: ** ** This routine sets device characteristics of the LTA and TT devices. ** The HOSTSYNC, NOBRDCST, EIGHTBIT and PASTHRU characteristics are set ** on the LTA device. The ESCAPE and TTSYNC characteristics are cleared. ** ** The TTSYNC, HOSTSYNC, EIGHTBIT, and PASTHRU characteristics are set ** on the TT device. The ESCAPE characteristic is cleared. The TT ** characterisitcs are also saved for restoration at program exit. ** **-- */ unsigned long SetDeviceChars(void) { /* ** Local Variables: */ unsigned long status, deviceChar[3]; /* ** BEGIN: ** ** Mask and set the characteristics of the LTA device. Sense the ** current characteristics, and mask in and set the new ones. */ if (!((status = sys$qiow( 0, LTAchannel, IO$_SENSEMODE, <A_QIOiosb, 0, 0, &deviceChar, DeviceCharBuffSize, 0, 0, 0, 0)) & 1)) lib$signal(status); if (!(LTA_QIOiosb[0] & 1)) lib$signal(LTA_QIOiosb[0]); deviceChar[1] = (deviceChar[1] | (TT$M_HOSTSYNC | TT$M_NOBRDCST | TT$M_EIGHTBIT)) & ~TT$M_ESCAPE & ~TT$M_TTSYNC; deviceChar[2] |= TT2$M_PASTHRU; if (!((status = sys$qiow( 0, LTAchannel, IO$_SETMODE, <A_QIOiosb, 0, 0, &deviceChar, DeviceCharBuffSize, 0, 0, 0, 0)) & 1)) lib$signal(status); if (!(LTA_QIOiosb[0] & 1)) lib$signal(LTA_QIOiosb[0]); /* ** Repeat the procedure for TT device characteristics. However, save ** the current characteristics for restoration at program exit. */ if (!((status = sys$qiow( 0, TTchannel, IO$_SENSEMODE, &TT_QIOiosb, 0, 0, &SavedTTdeviceChar, DeviceCharBuffSize, 0, 0, 0, 0)) & 1)) lib$signal(status); if (!(TT_QIOiosb[0] & 1)) lib$signal(TT_QIOiosb[0]); deviceChar[0] = SavedTTdeviceChar[0]; deviceChar[1] = (SavedTTdeviceChar[1] | (TT$M_TTSYNC | TT$M_HOSTSYNC | TT$M_EIGHTBIT)) & ~TT$M_ESCAPE; deviceChar[2] = SavedTTdeviceChar[2] | TT2$M_PASTHRU; if (!((status = sys$qiow( 0, TTchannel, IO$_SETMODE, &TT_QIOiosb, 0, 0, &deviceChar, DeviceCharBuffSize, 0, 0, 0, 0)) & 1)) lib$signal(status); if (!(TT_QIOiosb[0] & 1)) lib$signal(TT_QIOiosb[0]); return(status); } /* END - SetDeviceChars */ /* **++ ** ** FUNCTIONAL DESCRIPTION: ** ** This routine is an AST which executes when the connect $QIO completes. ** First the IOSB is checked. If the connection timed out or was aborted, ** simply end the session. Any other abnormal status causes the program ** to exit. ** ** Otherwise the connection completed successfully and a read on the TT ** channel is posted. ** **-- */ void ConnectAST() { /* ** Local Variables: */ unsigned long status; /* ** BEGIN: ** ** If the status in the IOSB indicates that the connection timed out ** or aborted, call the session end routine. Any other abnormal ** status causes program exit. */ if ((LTA_QIOiosb[0] == SS$_TIMEOUT) || (LTA_QIOiosb[0] == SS$_ABORT)) EndSession(); if (!(LTA_QIOiosb[0] & 1)) sys$exit(LTA_QIOiosb[0]); /* ** The connection completed successfully! Post a read (with AST) on ** the TT device and return. */ if (!((status = sys$qio( 0, TTchannel, IO$_READVBLK|IO$M_NOECHO, &TT_QIOiosb, TTreadChannelAST, 0, TTbuffer, 1, 0, &ReadTerminatorMask, 0, 0)) & 1)) lib$signal(status); return; } /* END - ConnectAST */ /* **++ ** ** FUNCTIONAL DESCRIPTION: ** ** This routine is an AST which executes when the first character read on ** the LTA channel completes. It does a "flush" read of the channel to ** drain any data out of the ALTYPAHD buffer and writes the data to the ** TT channel. It then posts another read on the channel. ** **-- */ void LTAreadChannelAST(void) { /* ** Local Variables: */ unsigned long status; /* ** BEGIN: ** ** If the status in the IOSB indicates channel hangup, simply end the ** session. Signal any other abnormal status. */ if (LTA_QIOiosb[0] == SS$_HANGUP) EndSession(); if (!(LTA_QIOiosb[0] & 1)) lib$signal(LTA_QIOiosb[0]); /* ** Do a "flush" read of the LTA device. This is done by doing a timed ** read with a 0 timeout. There may or may not be any data to drain. ** This method is more efficient than using single character reads. */ if (!((status = sys$qiow( 0, LTAchannel, IO$_READVBLK|IO$M_TIMED|IO$M_NOECHO, <A_QIOiosb, 0, 0, LTAbuffer+1, LTA_MAXBUF-1, 0, &ReadTerminatorMask, 0, 0)) & 1)) lib$signal(status); if (!(LTA_QIOiosb[0] & 1) && (LTA_QIOiosb[0] != SS$_TIMEOUT)) lib$signal(LTA_QIOiosb[0]); /* ** The second word of the IOSB contains the number of characters ** read. Write the characters plus 1 for the initial read to the ** TT device. */ if (!((status = sys$qiow( 0, TTchannel, IO$_WRITEVBLK, &TT_QIOiosb, 0, 0, LTAbuffer, LTA_QIOiosb[1]+1, 0, 0, 0, 0)) & 1)) lib$signal(status); if (!(TT_QIOiosb[0] & 1)) lib$signal(TT_QIOiosb[0]); /* ** Post another read on the LTA device. */ if (!((status = sys$qio( 0, LTAchannel, IO$_READVBLK|IO$M_NOECHO, <A_QIOiosb, LTAreadChannelAST, 0, LTAbuffer, 1, 0, &ReadTerminatorMask, 0, 0)) & 1)) lib$signal(status); return; } /* END - LTAreadChannelAST */ /* **++ ** ** FUNCTIONAL DESCRIPTION: ** ** This routine is an AST which executes when the first character read on ** the TT channel completes. It does a "flush" read of the channel to ** drain any data out of the TYPAHD buffer and writes the data to the ** LTA channel. It then posts another read on the channel. ** **-- */ void TTreadChannelAST(void) { /* ** Local Variables: */ unsigned long status; /* ** BEGIN: ** ** If the user pressed the connection terminator character, do a LAT ** disconnect $QIO and exit. */ if (*TTbuffer == CONNECTION_TERMINATOR) { if (!((status = sys$qiow( 0, LTAchannel, IO$_TTY_PORT|IO$M_LT_DISCON, <A_QIOiosb, 0, 0, 0, 0, 0, 0, 0, 0)) & 1)) lib$signal(status); if (!(LTA_QIOiosb[0] & 1)) lib$signal(LTA_QIOiosb[0]); return; } /* ** Do a "flush" read of the TT device. This is done by doing a timed ** read with a 0 timeout. There may or may not be any data to drain. */ if (!((status = sys$qiow( 0, TTchannel, IO$_READVBLK|IO$M_TIMED|IO$M_NOECHO, &TT_QIOiosb, 0, 0, TTbuffer+1, TT_MAXBUF-1, 0, &ReadTerminatorMask, 0, 0)) & 1)) lib$signal(status); if (!(TT_QIOiosb[0] & 1) && (TT_QIOiosb[0] != SS$_TIMEOUT)) lib$signal(TT_QIOiosb[0]); /* ** The second word of the IOSB contains the number of characters ** read. Write the characters plus 1 for the initial read to the ** TT device. */ if (!((status = sys$qiow( 0, LTAchannel, IO$_WRITEVBLK, <A_QIOiosb, 0, 0, TTbuffer, TT_QIOiosb[1]+1, 0, 0, 0, 0)) & 1)) lib$signal(status); /* ** If the status in the IOSB indicates channel hangup, simply end ** the session. Signal any other abnormal status. */ if (LTA_QIOiosb[0] == SS$_HANGUP) EndSession(); if (!(LTA_QIOiosb[0] & 1)) lib$signal(LTA_QIOiosb[0]); /* ** Post another read on the LTA device. */ if (!((status = sys$qio( 0, TTchannel, IO$_READVBLK|IO$M_NOECHO, &TT_QIOiosb, TTreadChannelAST, 0, TTbuffer, 1, 0, &ReadTerminatorMask, 0, 0)) & 1)) lib$signal(status); return; } /* END - TTreadChannelAST */ /* **++ ** ** FUNCTIONAL DESCRIPTION: ** ** This routine is the CTRL+Y AST for the LTA channel. It executes when ** a hangup on the LTA channel is recognized (connection timed out or ** aborted). It will call the session end routine if it hasn't already ** been called by ConnectAST. ** ** NOTE: CTRL+Y ASTs for application ports will NOT execute when the ** connection is disconnected. ** **-- */ void LTAhangupHandler(void) { /* ** BEGIN: ** ** Call the session end routine and return. */ EndSession(); return; } /* END - LTAhanghupHandler */ /* **++ ** ** FUNCTIONAL DESCRIPTION: ** ** This routine is executed at session end. It will do a $QIO SENSEmode ** and search the resulting itemlist to find the reason for the LAT ** disconnect. The reason for the disconnect is displayed on the ** terminal and the image exits. ** **-- */ void EndSession(void) { /* ** Local Variables: */ struct ITEM_ENTRY *itemlistEntry; unsigned long status; char *senseItemlist = malloc(MAX_SENSE_ITEMLIST_SIZE), *itemlistEntryPointer; /* ** BEGIN: ** ** Do the SENSEmode on the port. */ if (!((status = sys$qiow( 0, LTAchannel, IO$_TTY_PORT|IO$M_LT_SENSEMODE, <A_QIOiosb, 0, 0, senseItemlist, MAX_SENSE_ITEMLIST_SIZE, LAT$C_ENT_PORT|(LAT$M_SENSE_FULL << 0x10), 0, 0, 0)) & 1)) lib$signal(status); if (!(LTA_QIOiosb[0] & 1)) lib$signal(LTA_QIOiosb[0]); /* ** Set up two pointers used to traverse the itemlist. */ itemlistEntry = (struct ITEM_ENTRY *) senseItemlist; itemlistEntryPointer = senseItemlist; /* ** Search the itemlist for the LAT$_ITM_DISCONNECT_REASON code to find ** out why the connection terminated. */ while (itemlistEntry->LAT$R_ITM_CODE.LAT$W_ITEMCODE != LAT$_ITM_DISCONNECT_REASON) { /* ** If the current itemcode being checked has a string value, ** advance the pointer to the next itemcode by skipping ** BCNT bytes plus 3 bytes for the BCNT byte itself and the ** 2 byte itemcode. */ if (itemlistEntry-> LAT$R_ITM_CODE.LAT$R_ITM_BITS.LAT$V_STRING) itemlistEntryPointer += itemlistEntry->LAT$R_ITEM_VALUE. LAT$R_ITEM_COUNTED_STRING.LAT$B_ITEM_BCNT + 3; /* ** If the current itemcode being checked has a scalar value, ** advance the pointer to the next itemcode by skipping 6 ** bytes for the itemcode and the 4 byte scalar. */ else itemlistEntryPointer += 6; itemlistEntry = (struct ITEM_ENTRY *) itemlistEntryPointer; } /* ** If the disconnect reason is a LAT reject code, print out the ** text corresponding to the code and set the exit condition value ** to SS$_NORMAL. */ if (itemlistEntry->LAT$R_ITEM_VALUE.LAT$L_ITEM_SCALAR_VALUE <= LATrejectTableSize) { printf("\nSession disconnected. Reason: %s\n\n\n", LATrejectTable[ itemlistEntry->LAT$R_ITEM_VALUE. LAT$L_ITEM_SCALAR_VALUE ]); ExitConditionValue = SS$_NORMAL; } /* ** The scalar value is a LAT facility message code. Set the exit ** condition value to be the scalar. Upon image exit, the ** corresponding LAT facility message will be displayed. */ else ExitConditionValue = itemlistEntry->LAT$R_ITEM_VALUE.LAT$L_ITEM_SCALAR_VALUE; sys$exit(ExitConditionValue); } /* END - EndSession */ /* **++ ** ** FUNCTIONAL DESCRIPTION: ** ** This is the program exit handler which is executed upon image exit. ** It will cancel all pending I/O on the two channels and restore the ** TT channel characteristics. ** **-- */ void ExitHandler(void) { /* ** Local Variables: */ unsigned long status; /* ** BEGIN: ** ** Cancel I/O on the channels, reset terminal characteristics and ** return. */ if (!((status = sys$cancel(LTAchannel)) & 1)) lib$signal(status); if (!((status = sys$cancel(TTchannel)) & 1)) lib$signal(status); if (!((status = sys$qiow( 0, TTchannel, IO$_SETMODE, &TT_QIOiosb, 0, 0, &SavedTTdeviceChar, DeviceCharBuffSize, 0, 0, 0, 0)) & 1)) lib$signal(status); if (!(TT_QIOiosb[0] & 1)) lib$signal(TT_QIOiosb[0]); return; } /* END - ExitHandler */