mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-05-10 12:21:43 +02:00
2345 lines
84 KiB
C
2345 lines
84 KiB
C
/* CONSOLE.C (c)Copyright Roger Bowler, 1999-2002 */
|
|
/* ESA/390 Console Device Handler */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module contains device handling functions for console */
|
|
/* devices for the Hercules ESA/390 emulator. */
|
|
/* */
|
|
/* Telnet support is provided for two classes of console device: */
|
|
/* - local non-SNA 3270 display consoles via tn3270 */
|
|
/* - 1052 and 3215 console printer keyboards via regular telnet */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module also takes care of the differences between the */
|
|
/* remote 3270 and local non-SNA 3270 devices. In particular */
|
|
/* the support of command chaining, which is not supported on */
|
|
/* the remote 3270 implementation on which telnet 3270 is based. */
|
|
/* In the local non-SNA environment a chained read or write will */
|
|
/* continue at the buffer address where the previous command ended. */
|
|
/* In order to achieve this, this module will keep track of the */
|
|
/* buffer location, and adjust the buffer address on chained read */
|
|
/* and write operations. */
|
|
/* 03/06/00 Jan Jaeger. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Add code to bypass bug in telnet client from TCP/IP for OS/390 */
|
|
/* where telnet responds with the server response rather then the */
|
|
/* client response as documented in RFC1576. */
|
|
/* 20/06/00 Jan Jaeger. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Corrections to buffer position calculations in find_buffer_pos */
|
|
/* and get_screen_pos subroutines (symptom: message IEE305I) */
|
|
/* 09/12/00 Roger Bowler. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* When using VTAM with local non-SNA 3270 devices, ensure that */
|
|
/* enough bufferspace is available when doing IND$FILE type */
|
|
/* filetransfers. Code IOBUF=(,3992) in ATCSTRxx, and/or BUFNUM=xxx */
|
|
/* on the LBUILD LOCAL statement defining the 3270 device. JJ */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hercules.h"
|
|
|
|
#include "devtype.h"
|
|
|
|
#include "opcode.h"
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Telnet command definitions */
|
|
/*-------------------------------------------------------------------*/
|
|
#define BINARY 0 /* Binary Transmission */
|
|
#define IS 0 /* Used by terminal-type negotiation */
|
|
#define SEND 1 /* Used by terminal-type negotiation */
|
|
#define ECHO_OPTION 1 /* Echo option */
|
|
#define SUPPRESS_GA 3 /* Suppress go-ahead option */
|
|
#define TIMING_MARK 6 /* Timing mark option */
|
|
#define TERMINAL_TYPE 24 /* Terminal type option */
|
|
#define EOR 25 /* End of record option */
|
|
#define EOR_MARK 239 /* End of record marker */
|
|
#define SE 240 /* End of subnegotiation parameters */
|
|
#define NOP 241 /* No operation */
|
|
#define DATA_MARK 242 /* The data stream portion of a Synch.
|
|
This should always be accompanied
|
|
by a TCP Urgent notification */
|
|
#define BRK 243 /* Break character */
|
|
#define IP 244 /* Interrupt Process */
|
|
#define AO 245 /* Abort Output */
|
|
#define AYT 246 /* Are You There */
|
|
#define EC 247 /* Erase character */
|
|
#define EL 248 /* Erase Line */
|
|
#define GA 249 /* Go ahead */
|
|
#define SB 250 /* Subnegotiation of indicated option */
|
|
#define WILL 251 /* Indicates the desire to begin
|
|
performing, or confirmation that
|
|
you are now performing, the
|
|
indicated option */
|
|
#define WONT 252 /* Indicates the refusal to perform,
|
|
or continue performing, the
|
|
indicated option */
|
|
#define DO 253 /* Indicates the request that the
|
|
other party perform, or
|
|
confirmation that you are expecting
|
|
the other party to perform, the
|
|
indicated option */
|
|
#define DONT 254 /* Indicates the demand that the
|
|
other party stop performing,
|
|
or confirmation that you are no
|
|
longer expecting the other party
|
|
to perform, the indicated option */
|
|
#define IAC 255 /* Interpret as Command */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* 3270 definitions */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/* 3270 local commands (CCWs) */
|
|
#define L3270_EAU 0x0F /* Erase All Unprotected */
|
|
#define L3270_EW 0x05 /* Erase/Write */
|
|
#define L3270_EWA 0x0D /* Erase/Write Alternate */
|
|
#define L3270_RB 0x02 /* Read Buffer */
|
|
#define L3270_RM 0x06 /* Read Modified */
|
|
#define L3270_WRT 0x01 /* Write */
|
|
#define L3270_WSF 0x11 /* Write Structured Field */
|
|
|
|
#define L3270_NOP 0x03 /* No Operation */
|
|
#define L3270_SELRM 0x0B /* Select RM */
|
|
#define L3270_SELRB 0x1B /* Select RB */
|
|
#define L3270_SELRMP 0x2B /* Select RMP */
|
|
#define L3270_SELRBP 0x3B /* Select RBP */
|
|
#define L3270_SELWRT 0x4B /* Select WRT */
|
|
#define L3270_SENSE 0x04 /* Sense */
|
|
#define L3270_SENSEID 0xE4 /* Sense ID */
|
|
|
|
/* 3270 remote commands */
|
|
#define R3270_EAU 0x6F /* Erase All Unprotected */
|
|
#define R3270_EW 0xF5 /* Erase/Write */
|
|
#define R3270_EWA 0x7E /* Erase/Write Alternate */
|
|
#define R3270_RB 0xF2 /* Read Buffer */
|
|
#define R3270_RM 0xF6 /* Read Modified */
|
|
#define R3270_RMA 0x6E /* Read Modified All */
|
|
#define R3270_WRT 0xF1 /* Write */
|
|
#define R3270_WSF 0xF3 /* Write Structured Field */
|
|
|
|
/* 3270 orders */
|
|
#define O3270_SBA 0x11 /* Set Buffer Address */
|
|
#define O3270_SF 0x1D /* Start Field */
|
|
#define O3270_SFE 0x29 /* Start Field Extended */
|
|
#define O3270_SA 0x28 /* Set Attribute */
|
|
#define O3270_IC 0x13 /* Insert Cursor */
|
|
#define O3270_MF 0x2C /* Modify Field */
|
|
#define O3270_PT 0x05 /* Program Tab */
|
|
#define O3270_RA 0x3C /* Repeat to Address */
|
|
#define O3270_EUA 0x12 /* Erase Unprotected to Addr */
|
|
#define O3270_GE 0x08 /* Graphic Escape */
|
|
|
|
/* Inbound structured fields */
|
|
#define SF3270_AID 0x88 /* Aid value of inbound SF */
|
|
#define SF3270_3270DS 0x80 /* SFID of 3270 datastream SF*/
|
|
|
|
/* 12 bit 3270 buffer address code conversion table */
|
|
static BYTE sba_code[] = { "\x40\xC1\xC2\xC3\xC4\xC5\xC6\xC7"
|
|
"\xC8\xC9\x4A\x4B\x4C\x4D\x4E\x4F"
|
|
"\x50\xD1\xD2\xD3\xD4\xD5\xD6\xD7"
|
|
"\xD8\xD9\x5A\x5B\x5C\x5D\x5E\x5F"
|
|
"\x60\x61\xE2\xE3\xE4\xE5\xE6\xE7"
|
|
"\xE8\xE9\x6A\x6B\x6C\x6D\x6E\x6F"
|
|
"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7"
|
|
"\xF8\xF9\x7A\x7B\x7C\x7D\x7E\x7F" };
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Internal macro definitions */
|
|
/*-------------------------------------------------------------------*/
|
|
#define DEBUG_LVL 0 /* 1 = status
|
|
2 = headers
|
|
3 = buffers */
|
|
#if DEBUG_LVL == 0
|
|
#define TNSDEBUG1(_format, _args...)
|
|
#define TNSDEBUG2(_format, _args...)
|
|
#define TNSDEBUG3(_format, _args...)
|
|
#endif
|
|
#if DEBUG_LVL == 1
|
|
#define TNSDEBUG1(_format, _args...) \
|
|
logmsg("console: " _format, ## _args)
|
|
#define TNSDEBUG2(_format, _args...)
|
|
#define TNSDEBUG3(_format, _args...)
|
|
#endif
|
|
#if DEBUG_LVL == 2
|
|
#define TNSDEBUG1(_format, _args...) \
|
|
logmsg("console: " _format, ## _args)
|
|
#define TNSDEBUG2(_format, _args...) \
|
|
logmsg("console: " _format, ## _args)
|
|
#define TNSDEBUG3(_format, _args...)
|
|
#endif
|
|
#if DEBUG_LVL == 3
|
|
#define TNSDEBUG1(_format, _args...) \
|
|
logmsg("console: " _format, ## _args)
|
|
#define TNSDEBUG2(_format, _args...) \
|
|
logmsg("console: " _format, ## _args)
|
|
#define TNSDEBUG3(_format, _args...) \
|
|
logmsg("console: " _format, ## _args)
|
|
#endif
|
|
|
|
#define TNSERROR(_format,_args...) \
|
|
logmsg("console: " _format, ## _args)
|
|
|
|
#define BUFLEN_3270 65536 /* 3270 Send/Receive buffer */
|
|
#define BUFLEN_1052 150 /* 1052 Send/Receive buffer */
|
|
#define SPACE ((BYTE)' ')
|
|
|
|
|
|
#undef FIX_QWS_BUG_FOR_MCS_CONSOLES
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Static data areas */
|
|
/*-------------------------------------------------------------------*/
|
|
static struct utsname hostinfo; /* Host info for this system */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO TRACE THE CONTENTS OF AN ASCII MESSAGE PACKET */
|
|
/*-------------------------------------------------------------------*/
|
|
#if DEBUG_LVL == 3
|
|
static void
|
|
packet_trace(BYTE *addr, int len)
|
|
{
|
|
unsigned int i, offset;
|
|
unsigned char c;
|
|
unsigned char print_chars[17];
|
|
|
|
for (offset=0; offset < len; )
|
|
{
|
|
memset(print_chars,0,sizeof(print_chars));
|
|
logmsg("+%4.4X ", offset);
|
|
for (i=0; i < 16; i++)
|
|
{
|
|
c = *addr++;
|
|
if (offset < len) {
|
|
logmsg("%2.2X", c);
|
|
print_chars[i] = '.';
|
|
if (isprint(c)) print_chars[i] = c;
|
|
c = guest_to_host(c);
|
|
if (isprint(c)) print_chars[i] = c;
|
|
}
|
|
else {
|
|
logmsg(" ");
|
|
}
|
|
offset++;
|
|
if ((offset & 3) == 0) {
|
|
logmsg(" ");
|
|
}
|
|
} /* end for(i) */
|
|
logmsg(" %s\n", print_chars);
|
|
} /* end for(offset) */
|
|
|
|
} /* end function packet_trace */
|
|
#else
|
|
#define packet_trace( _addr, _len)
|
|
#endif
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO REMOVE ANY IAC SEQUENCES FROM THE DATA STREAM */
|
|
/* Returns the new length after deleting IAC commands */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
remove_iac (BYTE *buf, int len)
|
|
{
|
|
int m, n, c;
|
|
|
|
for (m=0, n=0; m < len; ) {
|
|
/* Interpret IAC commands */
|
|
if (buf[m] == IAC) {
|
|
/* Treat IAC in last byte of buffer as IAC NOP */
|
|
c = (++m < len)? buf[m++] : NOP;
|
|
/* Process IAC command */
|
|
switch (c) {
|
|
case IAC: /* Insert single IAC in buffer */
|
|
buf[n++] = IAC;
|
|
break;
|
|
case BRK: /* Set ATTN indicator */
|
|
break;
|
|
case IP: /* Set SYSREQ indicator */
|
|
break;
|
|
case WILL: /* Skip option negotiation command */
|
|
case WONT:
|
|
case DO:
|
|
case DONT:
|
|
m++;
|
|
break;
|
|
case SB: /* Skip until IAC SE sequence found */
|
|
for (; m < len; m++) {
|
|
if (buf[m] != IAC) continue;
|
|
if (++m >= len) break;
|
|
if (buf[m] == SE) { m++; break; }
|
|
} /* end for */
|
|
default: /* Ignore NOP or unknown command */
|
|
break;
|
|
} /* end switch(c) */
|
|
} else {
|
|
/* Copy data bytes */
|
|
if (n < m) buf[n] = buf[m];
|
|
m++; n++;
|
|
}
|
|
} /* end for */
|
|
|
|
if (n < m) {
|
|
TNSDEBUG3("%d IAC bytes removed, newlen=%d\n", m-n, n);
|
|
packet_trace (buf, n);
|
|
}
|
|
|
|
return n;
|
|
|
|
} /* end function remove_iac */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO DOUBLE UP ANY IAC BYTES IN THE DATA STREAM */
|
|
/* Returns the new length after inserting extra IAC bytes */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
double_up_iac (BYTE *buf, int len)
|
|
{
|
|
int m, n, x, newlen;
|
|
|
|
/* Count the number of IAC bytes in the data */
|
|
for (x=0, n=0; n < len; n++)
|
|
if (buf[n] == IAC) x++;
|
|
|
|
/* Exit if nothing to do */
|
|
if (x == 0) return len;
|
|
|
|
/* Insert extra IAC bytes backwards from the end of the buffer */
|
|
newlen = len + x;
|
|
TNSDEBUG3("%d IAC bytes added, newlen=%d\n", x, newlen);
|
|
for (n=newlen, m=len; n > m; ) {
|
|
buf[--n] = buf[--m];
|
|
if (buf[n] == IAC) buf[--n] = IAC;
|
|
}
|
|
packet_trace (buf, newlen);
|
|
return newlen;
|
|
|
|
} /* end function double_up_iac */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO TRANSLATE A NULL-TERMINATED STRING TO EBCDIC */
|
|
/*-------------------------------------------------------------------*/
|
|
static BYTE *
|
|
translate_to_ebcdic (BYTE *str)
|
|
{
|
|
int i; /* Array subscript */
|
|
BYTE c; /* Character work area */
|
|
|
|
for (i = 0; str[i] != '\0'; i++)
|
|
{
|
|
c = str[i];
|
|
str[i] = (isprint(c) ? host_to_guest(c) : SPACE);
|
|
}
|
|
|
|
return str;
|
|
} /* end function translate_to_ebcdic */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO SEND A DATA PACKET TO THE CLIENT */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
send_packet (int csock, BYTE *buf, int len, char *caption)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
if (caption != NULL) {
|
|
TNSDEBUG2("Sending %s\n", caption);
|
|
packet_trace (buf, len);
|
|
}
|
|
|
|
rc = send (csock, buf, len, 0);
|
|
|
|
if (rc < 0) {
|
|
TNSERROR("send: %s\n", strerror(errno));
|
|
return -1;
|
|
} /* end if(rc) */
|
|
|
|
return 0;
|
|
|
|
} /* end function send_packet */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO RECEIVE A DATA PACKET FROM THE CLIENT */
|
|
/* This subroutine receives bytes from the client. It stops when */
|
|
/* the receive buffer is full, or when the last two bytes received */
|
|
/* consist of the IAC character followed by a specified delimiter. */
|
|
/* If zero bytes are received, this means the client has closed the */
|
|
/* connection, and this is treated as an error. */
|
|
/* Input: */
|
|
/* csock is the socket number */
|
|
/* buf points to area to receive data */
|
|
/* reqlen is the number of bytes requested */
|
|
/* delim is the delimiter character (0=no delimiter) */
|
|
/* Output: */
|
|
/* buf is updated with data received */
|
|
/* The return value is the number of bytes received, or */
|
|
/* -1 if an error occurred. */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
recv_packet (int csock, BYTE *buf, int reqlen, BYTE delim)
|
|
{
|
|
int rc=0; /* Return code */
|
|
int rcvlen=0; /* Length of data received */
|
|
|
|
while (rcvlen < reqlen) {
|
|
|
|
rc = recv (csock, buf + rcvlen, reqlen - rcvlen, 0);
|
|
|
|
if (rc < 0) {
|
|
TNSERROR("recv: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (rc == 0) {
|
|
TNSDEBUG1("Connection closed by client\n");
|
|
return -1;
|
|
}
|
|
|
|
rcvlen += rc;
|
|
|
|
if (delim != '\0' && rcvlen >= 2
|
|
&& buf[rcvlen-2] == IAC && buf[rcvlen-1] == delim)
|
|
break;
|
|
}
|
|
|
|
TNSDEBUG2("Packet received length=%d\n", rcvlen);
|
|
packet_trace (buf, rcvlen);
|
|
|
|
return rcvlen;
|
|
|
|
} /* end function recv_packet */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO RECEIVE A PACKET AND COMPARE WITH EXPECTED VALUE */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
expect (int csock, BYTE *expected, int len, char *caption)
|
|
{
|
|
int rc; /* Return code */
|
|
BYTE buf[512]; /* Receive buffer */
|
|
#if 1
|
|
/* TCP/IP for MVS returns the server sequence rather then
|
|
the client sequence during bin negotiation 19/06/00 Jan Jaeger */
|
|
static BYTE do_bin[] = { IAC, DO, BINARY, IAC, WILL, BINARY };
|
|
static BYTE will_bin[] = { IAC, WILL, BINARY, IAC, DO, BINARY };
|
|
#endif
|
|
|
|
rc = recv_packet (csock, buf, len, 0);
|
|
if (rc < 0) return -1;
|
|
|
|
#if 1
|
|
/* TCP/IP FOR MVS DOES NOT COMPLY TO RFC 1576 THIS IS A BYPASS */
|
|
if(memcmp(buf, expected, len) != 0
|
|
&& !(len == sizeof(will_bin)
|
|
&& memcmp(expected, will_bin, len) == 0
|
|
&& memcmp(buf, do_bin, len) == 0) )
|
|
#else
|
|
if (memcmp(buf, expected, len) != 0)
|
|
#endif
|
|
{
|
|
TNSDEBUG2("Expected %s\n", caption);
|
|
return -1;
|
|
}
|
|
TNSDEBUG2("Received %s\n", caption);
|
|
|
|
return 0;
|
|
|
|
} /* end function expect */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO NEGOTIATE TELNET PARAMETERS */
|
|
/* This subroutine negotiates the terminal type with the client */
|
|
/* and uses the terminal type to determine whether the client */
|
|
/* is to be supported as a 3270 display console or as a 1052/3215 */
|
|
/* printer-keyboard console. */
|
|
/* */
|
|
/* Valid display terminal types are "IBM-NNNN", "IBM-NNNN-M", and */
|
|
/* "IBM-NNNN-M-E", where NNNN is 3270, 3277, 3278, 3279, 3178, 3179, */
|
|
/* or 3180, M indicates the screen size (2=25x80, 3=32x80, 4=43x80, */
|
|
/* 5=27x132, X=determined by Read Partition Query command), and */
|
|
/* -E is an optional suffix indicating that the terminal supports */
|
|
/* extended attributes. Displays are negotiated into tn3270 mode. */
|
|
/* An optional device number suffix (example: IBM-3270@01F) may */
|
|
/* be specified to request allocation to a specific device number. */
|
|
/* */
|
|
/* Terminal types whose first four characters are not "IBM-" are */
|
|
/* handled as printer-keyboard consoles using telnet line mode. */
|
|
/* */
|
|
/* Input: */
|
|
/* csock Socket number for client connection */
|
|
/* Output: */
|
|
/* class D=3270 display console, K=printer-keyboard console */
|
|
/* model 3270 model indicator (2,3,4,5,X) */
|
|
/* extatr 3270 extended attributes (Y,N) */
|
|
/* devn Requested device number, or FFFF=any device number */
|
|
/* Return value: */
|
|
/* 0=negotiation successful, -1=negotiation error */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
negotiate(int csock, BYTE *class, BYTE *model, BYTE *extatr, U16 *devn)
|
|
{
|
|
int rc; /* Return code */
|
|
BYTE *termtype; /* Pointer to terminal type */
|
|
BYTE *s; /* String pointer */
|
|
U16 devnum; /* Requested device number */
|
|
BYTE buf[512]; /* Telnet negotiation buffer */
|
|
static BYTE do_term[] = { IAC, DO, TERMINAL_TYPE };
|
|
static BYTE will_term[] = { IAC, WILL, TERMINAL_TYPE };
|
|
static BYTE req_type[] = { IAC, SB, TERMINAL_TYPE, SEND, IAC, SE };
|
|
static BYTE type_is[] = { IAC, SB, TERMINAL_TYPE, IS };
|
|
static BYTE do_eor[] = { IAC, DO, EOR, IAC, WILL, EOR };
|
|
static BYTE will_eor[] = { IAC, WILL, EOR, IAC, DO, EOR };
|
|
static BYTE do_bin[] = { IAC, DO, BINARY, IAC, WILL, BINARY };
|
|
static BYTE will_bin[] = { IAC, WILL, BINARY, IAC, DO, BINARY };
|
|
#if 0
|
|
static BYTE do_tmark[] = { IAC, DO, TIMING_MARK };
|
|
static BYTE will_tmark[] = { IAC, WILL, TIMING_MARK };
|
|
static BYTE wont_sga[] = { IAC, WONT, SUPPRESS_GA };
|
|
static BYTE dont_sga[] = { IAC, DONT, SUPPRESS_GA };
|
|
#endif
|
|
static BYTE wont_echo[] = { IAC, WONT, ECHO_OPTION };
|
|
static BYTE dont_echo[] = { IAC, DONT, ECHO_OPTION };
|
|
|
|
/* Perform terminal-type negotiation */
|
|
rc = send_packet (csock, do_term, sizeof(do_term),
|
|
"IAC DO TERMINAL_TYPE");
|
|
if (rc < 0) return -1;
|
|
|
|
rc = expect (csock, will_term, sizeof(will_term),
|
|
"IAC WILL TERMINAL_TYPE");
|
|
if (rc < 0) return -1;
|
|
|
|
/* Request terminal type */
|
|
rc = send_packet (csock, req_type, sizeof(req_type),
|
|
"IAC SB TERMINAL_TYPE SEND IAC SE");
|
|
if (rc < 0) return -1;
|
|
|
|
rc = recv_packet (csock, buf, sizeof(buf)-2, SE);
|
|
if (rc < 0) return -1;
|
|
|
|
if (rc < sizeof(type_is) + 2
|
|
|| memcmp(buf, type_is, sizeof(type_is)) != 0
|
|
|| buf[rc-2] != IAC || buf[rc-1] != SE) {
|
|
TNSDEBUG2("Expected IAC SB TERMINAL_TYPE IS\n");
|
|
return -1;
|
|
}
|
|
buf[rc-2] = '\0';
|
|
termtype = buf + sizeof(type_is);
|
|
TNSDEBUG2("Received IAC SB TERMINAL_TYPE IS %s IAC SE\n",
|
|
termtype);
|
|
|
|
/* Check terminal type string for device name suffix */
|
|
s = strchr (termtype, '@');
|
|
if (s != NULL && sscanf (s, "@%hx", &devnum) == 1)
|
|
*devn = devnum;
|
|
else
|
|
*devn = 0xFFFF;
|
|
|
|
/* Test for non-display terminal type */
|
|
if (memcmp(termtype, "IBM-", 4) != 0)
|
|
{
|
|
#if 0
|
|
/* Perform line mode negotiation */
|
|
rc = send_packet (csock, do_tmark, sizeof(do_tmark),
|
|
"IAC DO TIMING_MARK");
|
|
if (rc < 0) return -1;
|
|
|
|
rc = expect (csock, will_tmark, sizeof(will_tmark),
|
|
"IAC WILL TIMING_MARK");
|
|
if (rc < 0) return 0;
|
|
|
|
rc = send_packet (csock, wont_sga, sizeof(wont_sga),
|
|
"IAC WONT SUPPRESS_GA");
|
|
if (rc < 0) return -1;
|
|
|
|
rc = expect (csock, dont_sga, sizeof(dont_sga),
|
|
"IAC DONT SUPPRESS_GA");
|
|
if (rc < 0) return -1;
|
|
#endif
|
|
|
|
if (memcmp(termtype, "ANSI", 4) == 0)
|
|
{
|
|
rc = send_packet (csock, wont_echo, sizeof(wont_echo),
|
|
"IAC WONT ECHO");
|
|
if (rc < 0) return -1;
|
|
|
|
rc = expect (csock, dont_echo, sizeof(dont_echo),
|
|
"IAC DONT ECHO");
|
|
if (rc < 0) return -1;
|
|
}
|
|
|
|
/* Return printer-keyboard terminal class */
|
|
*class = 'K';
|
|
*model = '-';
|
|
*extatr = '-';
|
|
return 0;
|
|
}
|
|
|
|
/* Determine display terminal model */
|
|
if (memcmp(termtype+4,"DYNAMIC",7) == 0) {
|
|
*model = 'X';
|
|
*extatr = 'Y';
|
|
} else {
|
|
if (!(memcmp(termtype+4, "3277", 4) == 0
|
|
|| memcmp(termtype+4, "3270", 4) == 0
|
|
|| memcmp(termtype+4, "3178", 4) == 0
|
|
|| memcmp(termtype+4, "3278", 4) == 0
|
|
|| memcmp(termtype+4, "3179", 4) == 0
|
|
|| memcmp(termtype+4, "3180", 4) == 0
|
|
|| memcmp(termtype+4, "3279", 4) == 0))
|
|
return -1;
|
|
|
|
*model = '2';
|
|
*extatr = 'N';
|
|
|
|
if (termtype[8]=='-') {
|
|
if (termtype[9] < '2' || termtype[9] > '5')
|
|
return -1;
|
|
*model = termtype[9];
|
|
if (memcmp(termtype+10, "-E", 2) == 0)
|
|
*extatr = 'Y';
|
|
}
|
|
}
|
|
|
|
/* Perform end-of-record negotiation */
|
|
rc = send_packet (csock, do_eor, sizeof(do_eor),
|
|
"IAC DO EOR IAC WILL EOR");
|
|
if (rc < 0) return -1;
|
|
|
|
rc = expect (csock, will_eor, sizeof(will_eor),
|
|
"IAC WILL EOR IAC DO EOR");
|
|
if (rc < 0) return -1;
|
|
|
|
/* Perform binary negotiation */
|
|
rc = send_packet (csock, do_bin, sizeof(do_bin),
|
|
"IAC DO BINARY IAC WILL BINARY");
|
|
if (rc < 0) return -1;
|
|
|
|
rc = expect (csock, will_bin, sizeof(will_bin),
|
|
"IAC WILL BINARY IAC DO BINARY");
|
|
if (rc < 0) return -1;
|
|
|
|
/* Return display terminal class */
|
|
*class = 'D';
|
|
return 0;
|
|
|
|
} /* end function negotiate */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO RECEIVE 3270 DATA FROM THE CLIENT */
|
|
/* This subroutine receives bytes from the client and appends them */
|
|
/* to any data already in the 3270 receive buffer. */
|
|
/* If zero bytes are received, this means the client has closed the */
|
|
/* connection, and attention and unit check status is returned. */
|
|
/* If the buffer is filled before receiving end of record, then */
|
|
/* attention and unit check status is returned. */
|
|
/* If the data ends with IAC followed by EOR_MARK, then the data */
|
|
/* is scanned to remove any IAC sequences, attention status is */
|
|
/* returned, and the read pending indicator is set. */
|
|
/* If the data accumulated in the buffer does not yet constitute a */
|
|
/* complete record, then zero status is returned, and a further */
|
|
/* call must be made to this subroutine when more data is available. */
|
|
/*-------------------------------------------------------------------*/
|
|
static BYTE
|
|
recv_3270_data (DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
int eor = 0; /* 1=End of record received */
|
|
|
|
/* If there is a complete data record already in the buffer
|
|
then discard it before reading more data */
|
|
if (dev->readpending)
|
|
{
|
|
dev->rlen3270 = 0;
|
|
dev->readpending = 0;
|
|
}
|
|
|
|
/* Receive bytes from client */
|
|
rc = recv (dev->fd, dev->buf + dev->rlen3270,
|
|
BUFLEN_3270 - dev->rlen3270, 0);
|
|
|
|
if (rc < 0) {
|
|
TNSERROR("recv: %s\n", strerror(errno));
|
|
dev->sense[0] = SENSE_EC;
|
|
return (CSW_ATTN | CSW_UC);
|
|
}
|
|
|
|
/* If zero bytes were received then client has closed connection */
|
|
if (rc == 0) {
|
|
logmsg (_("HHC603I Device %4.4X connection closed by client %s\n"),
|
|
dev->devnum, inet_ntoa(dev->ipaddr));
|
|
dev->sense[0] = SENSE_IR;
|
|
return (CSW_ATTN | CSW_UC | CSW_DE);
|
|
}
|
|
|
|
/* Update number of bytes in receive buffer */
|
|
dev->rlen3270 += rc;
|
|
|
|
/* Check whether Attn indicator was received */
|
|
if (dev->rlen3270 >= 2
|
|
&& dev->buf[dev->rlen3270 - 2] == IAC
|
|
&& dev->buf[dev->rlen3270 - 1] == BRK)
|
|
eor = 1;
|
|
|
|
/* Check whether SysRq indicator was received */
|
|
if (dev->rlen3270 >= 2
|
|
&& dev->buf[dev->rlen3270 - 2] == IAC
|
|
&& dev->buf[dev->rlen3270 - 1] == IP)
|
|
eor = 1;
|
|
|
|
/* Check whether end of record marker was received */
|
|
if (dev->rlen3270 >= 2
|
|
&& dev->buf[dev->rlen3270 - 2] == IAC
|
|
&& dev->buf[dev->rlen3270 - 1] == EOR_MARK)
|
|
eor = 1;
|
|
|
|
/* If record is incomplete, test for buffer full */
|
|
if (eor == 0 && dev->rlen3270 >= BUFLEN_3270)
|
|
{
|
|
TNSDEBUG1("3270 buffer overflow\n");
|
|
dev->sense[0] = SENSE_DC;
|
|
return (CSW_ATTN | CSW_UC);
|
|
}
|
|
|
|
/* Return zero status if record is incomplete */
|
|
if (eor == 0) return 0;
|
|
|
|
/* Trace the complete 3270 data packet */
|
|
TNSDEBUG2("Packet received length=%d\n", dev->rlen3270);
|
|
packet_trace (dev->buf, dev->rlen3270);
|
|
|
|
/* Strip off the telnet EOR marker */
|
|
dev->rlen3270 -= 2;
|
|
|
|
/* Remove any embedded IAC commands */
|
|
dev->rlen3270 = remove_iac (dev->buf, dev->rlen3270);
|
|
|
|
/* Set the read pending indicator and return attention status */
|
|
dev->readpending = 1;
|
|
return (CSW_ATTN);
|
|
|
|
} /* end function recv_3270_data */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO SOLICIT 3270 DATA FROM THE CLIENT */
|
|
/* This subroutine sends a Read or Read Modified command to the */
|
|
/* client and then receives the data into the 3270 receive buffer. */
|
|
/* This subroutine is called by loc3270_execute_ccw as a result of */
|
|
/* processing a Read Buffer CCW, or a Read Modified CCW when no */
|
|
/* data is waiting in the 3270 read buffer. It waits until the */
|
|
/* client sends end of record. Certain tn3270 clients fail to */
|
|
/* flush their buffer until the user presses an attention key; */
|
|
/* these clients cause this routine to hang and are not supported. */
|
|
/* Since this routine is only called while a channel program is */
|
|
/* active on the device, we can rely on the dev->busy flag to */
|
|
/* prevent the connection thread from issuing a read and capturing */
|
|
/* the incoming data intended for this routine. */
|
|
/* The caller MUST hold the device lock. */
|
|
/* Returns zero status if successful, or unit check if error. */
|
|
/*-------------------------------------------------------------------*/
|
|
static BYTE
|
|
solicit_3270_data (DEVBLK *dev, BYTE cmd)
|
|
{
|
|
int rc; /* Return code */
|
|
int len; /* Data length */
|
|
BYTE buf[32]; /* tn3270 write buffer */
|
|
|
|
/* Clear the inbound buffer of any unsolicited
|
|
data accumulated by the connection thread */
|
|
dev->rlen3270 = 0;
|
|
dev->readpending = 0;
|
|
|
|
/* Construct a 3270 read command in the outbound buffer */
|
|
len = 0;
|
|
buf[len++] = cmd;
|
|
|
|
/* Append telnet EOR marker to outbound buffer */
|
|
buf[len++] = IAC;
|
|
buf[len++] = EOR_MARK;
|
|
|
|
/* Send the 3270 read command to the client */
|
|
rc = send_packet(dev->fd, buf, len, "3270 Read Command");
|
|
if (rc < 0)
|
|
{
|
|
dev->sense[0] = SENSE_DC;
|
|
return (CSW_UC);
|
|
}
|
|
|
|
/* Receive response data from the client */
|
|
do {
|
|
len = dev->rlen3270;
|
|
rc = recv_3270_data (dev);
|
|
TNSDEBUG2("read buffer: %d bytes received\n",
|
|
dev->rlen3270 - len);
|
|
} while(rc == 0);
|
|
|
|
/* Close the connection if an error occurred */
|
|
if (rc & CSW_UC)
|
|
{
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
dev->connected = 0;
|
|
dev->sense[0] = SENSE_DC;
|
|
return (CSW_UC);
|
|
}
|
|
|
|
/* Return zero status to indicate response received */
|
|
return 0;
|
|
|
|
} /* end function solicit_3270_data */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO RECEIVE 1052/3215 DATA FROM THE CLIENT */
|
|
/* This subroutine receives keyboard input characters from the */
|
|
/* client, and appends the characters to any data already in the */
|
|
/* keyboard buffer. */
|
|
/* If zero bytes are received, this means the client has closed the */
|
|
/* connection, and attention and unit check status is returned. */
|
|
/* If the buffer is filled before receiving end of record, then */
|
|
/* attention and unit check status is returned. */
|
|
/* If a break indication (control-C, IAC BRK, or IAC IP) is */
|
|
/* received, the attention and unit exception status is returned. */
|
|
/* When carriage return and line feed (CRLF) is received, then */
|
|
/* the CRLF is discarded, the data in the keyboard buffer is */
|
|
/* translated to EBCDIC, the read pending indicator is set, and */
|
|
/* attention status is returned. */
|
|
/* If CRLF has not yet been received, then zero status is returned, */
|
|
/* and a further call must be made to this subroutine when more */
|
|
/* data is available. */
|
|
/*-------------------------------------------------------------------*/
|
|
static BYTE
|
|
recv_1052_data (DEVBLK *dev)
|
|
{
|
|
int num; /* Number of bytes received */
|
|
int i; /* Array subscript */
|
|
BYTE buf[BUFLEN_1052]; /* Receive buffer */
|
|
BYTE c; /* Character work area */
|
|
|
|
/* Receive bytes from client */
|
|
num = recv (dev->fd, buf, BUFLEN_1052, 0);
|
|
|
|
/* Return unit check if error on receive */
|
|
if (num < 0) {
|
|
TNSERROR("recv: %s\n", strerror(errno));
|
|
dev->sense[0] = SENSE_EC;
|
|
return (CSW_ATTN | CSW_UC);
|
|
}
|
|
|
|
/* If zero bytes were received then client has closed connection */
|
|
if (num == 0) {
|
|
logmsg (_("HHC606I Device %4.4X connection closed by client %s\n"),
|
|
dev->devnum, inet_ntoa(dev->ipaddr));
|
|
dev->sense[0] = SENSE_IR;
|
|
return (CSW_ATTN | CSW_UC);
|
|
}
|
|
|
|
/* Trace the bytes received */
|
|
TNSDEBUG2("Bytes received length=%d\n", num);
|
|
packet_trace (buf, num);
|
|
|
|
/* Copy received bytes to keyboard buffer */
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
/* Decrement keyboard buffer pointer if backspace received */
|
|
if (buf[i] == 0x08)
|
|
{
|
|
if (dev->keybdrem > 0) dev->keybdrem--;
|
|
continue;
|
|
}
|
|
|
|
/* Return unit exception if control-C received */
|
|
if (buf[i] == 0x03)
|
|
{
|
|
dev->keybdrem = 0;
|
|
return (CSW_ATTN | CSW_UX);
|
|
}
|
|
|
|
/* Return unit check if buffer is full */
|
|
if (dev->keybdrem >= BUFLEN_1052)
|
|
{
|
|
TNSDEBUG1("Console keyboard buffer overflow\n");
|
|
dev->keybdrem = 0;
|
|
dev->sense[0] = SENSE_EC;
|
|
return (CSW_ATTN | CSW_UC);
|
|
}
|
|
|
|
/* Copy character to keyboard buffer */
|
|
dev->buf[dev->keybdrem++] = buf[i];
|
|
|
|
/* Decrement keyboard buffer pointer if telnet
|
|
erase character sequence received */
|
|
if (dev->keybdrem >= 2
|
|
&& dev->buf[dev->keybdrem - 2] == IAC
|
|
&& dev->buf[dev->keybdrem - 1] == EC)
|
|
{
|
|
dev->keybdrem -= 2;
|
|
if (dev->keybdrem > 0) dev->keybdrem--;
|
|
continue;
|
|
}
|
|
|
|
/* Zeroize keyboard buffer pointer if telnet
|
|
erase line sequence received */
|
|
if (dev->keybdrem >= 2
|
|
&& dev->buf[dev->keybdrem - 2] == IAC
|
|
&& dev->buf[dev->keybdrem - 1] == EL)
|
|
{
|
|
dev->keybdrem = 0;
|
|
continue;
|
|
}
|
|
|
|
/* Zeroize keyboard buffer pointer if telnet
|
|
carriage return sequence received */
|
|
if (dev->keybdrem >= 2
|
|
&& dev->buf[dev->keybdrem - 2] == '\r'
|
|
&& dev->buf[dev->keybdrem - 1] == '\0')
|
|
{
|
|
dev->keybdrem = 0;
|
|
continue;
|
|
}
|
|
|
|
/* Return unit exception if telnet break sequence received */
|
|
if (dev->keybdrem >= 2
|
|
&& dev->buf[dev->keybdrem - 2] == IAC
|
|
&& (dev->buf[dev->keybdrem - 1] == BRK
|
|
|| dev->buf[dev->keybdrem - 1] == IP))
|
|
{
|
|
dev->keybdrem = 0;
|
|
return (CSW_ATTN | CSW_UX);
|
|
}
|
|
|
|
/* Return unit check with overrun if telnet CRLF
|
|
sequence received and more data follows the CRLF */
|
|
if (dev->keybdrem >= 2
|
|
&& dev->buf[dev->keybdrem - 2] == '\r'
|
|
&& dev->buf[dev->keybdrem - 1] == '\n'
|
|
&& i < num - 1)
|
|
{
|
|
TNSDEBUG1("Console keyboard buffer overrun\n");
|
|
dev->keybdrem = 0;
|
|
dev->sense[0] = SENSE_OR;
|
|
return (CSW_ATTN | CSW_UC);
|
|
}
|
|
|
|
} /* end for(i) */
|
|
|
|
/* Return zero status if CRLF was not yet received */
|
|
if (dev->keybdrem < 2
|
|
|| dev->buf[dev->keybdrem - 2] != '\r'
|
|
|| dev->buf[dev->keybdrem - 1] != '\n')
|
|
return 0;
|
|
|
|
/* Trace the complete keyboard data packet */
|
|
TNSDEBUG2("Packet received length=%d\n", dev->keybdrem);
|
|
packet_trace (dev->buf, dev->keybdrem);
|
|
|
|
/* Strip off the CRLF sequence */
|
|
dev->keybdrem -= 2;
|
|
|
|
/* Translate the keyboard buffer to EBCDIC */
|
|
for (i = 0; i < dev->keybdrem; i++)
|
|
{
|
|
c = dev->buf[i];
|
|
dev->buf[i] = (isprint(c) ? host_to_guest(c) : SPACE);
|
|
} /* end for(i) */
|
|
|
|
/* Trace the EBCDIC input data */
|
|
TNSDEBUG2("Input data line length=%d\n", dev->keybdrem);
|
|
packet_trace (dev->buf, dev->keybdrem);
|
|
|
|
/* Return attention status */
|
|
return (CSW_ATTN);
|
|
|
|
} /* end function recv_1052_data */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* NEW CLIENT CONNECTION THREAD */
|
|
/*-------------------------------------------------------------------*/
|
|
static void *
|
|
connect_client (int *csockp)
|
|
{
|
|
int rc; /* Return code */
|
|
DEVBLK *dev; /* -> Device block */
|
|
int len; /* Data length */
|
|
int csock; /* Socket for conversation */
|
|
struct sockaddr_in client; /* Client address structure */
|
|
socklen_t namelen; /* Length of client structure*/
|
|
struct hostent *pHE; /* Addr of hostent structure */
|
|
char *clientip; /* Addr of client ip address */
|
|
char *clientname; /* Addr of client hostname */
|
|
U16 devnum; /* Requested device number */
|
|
BYTE class; /* D=3270, K=3215/1052 */
|
|
BYTE model; /* 3270 model (2,3,4,5,X) */
|
|
BYTE extended; /* Extended attributes (Y,N) */
|
|
BYTE buf[256]; /* Message buffer */
|
|
BYTE conmsg[80]; /* Connection message */
|
|
BYTE hostmsg[80]; /* Host ID message */
|
|
BYTE rejmsg[80]; /* Rejection message */
|
|
|
|
/* Load the socket address from the thread parameter */
|
|
csock = *csockp;
|
|
|
|
/* Obtain the client's IP address */
|
|
namelen = sizeof(client);
|
|
rc = getpeername (csock, (struct sockaddr *)&client, &namelen);
|
|
|
|
/* Log the client's IP address and hostname */
|
|
clientip = inet_ntoa(client.sin_addr);
|
|
|
|
pHE = gethostbyaddr ((unsigned char*)(&client.sin_addr),
|
|
sizeof(client.sin_addr), AF_INET);
|
|
|
|
if (pHE != NULL && pHE->h_name != NULL
|
|
&& pHE->h_name[0] != '\0') {
|
|
clientname = (char*) pHE->h_name;
|
|
} else {
|
|
clientname = "host name unknown";
|
|
}
|
|
|
|
TNSDEBUG1("Received connection from %s (%s)\n",
|
|
clientip, clientname);
|
|
|
|
/* Negotiate telnet parameters */
|
|
rc = negotiate (csock, &class, &model, &extended, &devnum);
|
|
if (rc != 0)
|
|
{
|
|
close (csock);
|
|
return NULL;
|
|
}
|
|
|
|
/* Look for an available console device */
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
{
|
|
/* Loop if the device is invalid */
|
|
if(!(dev->pmcw.flag5 & PMCW5_V))
|
|
continue;
|
|
|
|
/* Loop if non-matching device type */
|
|
if (class == 'D' && dev->devtype != 0x3270)
|
|
continue;
|
|
|
|
if (class == 'K' && dev->devtype != 0x1052
|
|
&& dev->devtype != 0x3215)
|
|
continue;
|
|
|
|
/* Loop if a specific device number was requested and
|
|
this device is not the requested device number */
|
|
if (devnum != 0xFFFF && dev->devnum != devnum)
|
|
continue;
|
|
|
|
/* Obtain the device lock */
|
|
obtain_lock (&dev->lock);
|
|
|
|
/* Test for available device */
|
|
if (dev->connected == 0)
|
|
{
|
|
/* Claim this device for the client */
|
|
dev->connected = 1;
|
|
dev->fd = csock;
|
|
dev->ipaddr = client.sin_addr;
|
|
dev->mod3270 = model;
|
|
dev->eab3270 = (extended == 'Y' ? 1 : 0);
|
|
|
|
/* Reset the console device */
|
|
dev->readpending = 0;
|
|
dev->rlen3270 = 0;
|
|
dev->keybdrem = 0;
|
|
dev->busy = 0;
|
|
dev->pending = 0;
|
|
dev->pcipending = 0;
|
|
memset (&dev->scsw, 0, sizeof(SCSW));
|
|
memset (&dev->pciscsw, 0, sizeof(SCSW));
|
|
|
|
release_lock (&dev->lock);
|
|
break;
|
|
}
|
|
|
|
/* Release the device lock */
|
|
release_lock (&dev->lock);
|
|
|
|
} /* end for(dev) */
|
|
|
|
/* Build connection message for client */
|
|
len = snprintf (hostmsg, sizeof(hostmsg),
|
|
"running on %s (%s %s)",
|
|
hostinfo.nodename, hostinfo.sysname,
|
|
hostinfo.release);
|
|
len = snprintf (conmsg, sizeof(conmsg),
|
|
"Hercules version %s built at %s %s",
|
|
VERSION, __DATE__, __TIME__);
|
|
|
|
if (dev != NULL)
|
|
len += sprintf (conmsg + len, " device %4.4X", dev->devnum);
|
|
|
|
/* Reject the connection if no available console device */
|
|
if (dev == NULL)
|
|
{
|
|
/* Build the rejection message */
|
|
if (devnum == 0xFFFF)
|
|
len = sprintf (rejmsg,
|
|
"Connection rejected, no available %s device",
|
|
(class=='D' ? "3270" : "1052 or 3215"));
|
|
else
|
|
len = sprintf (rejmsg,
|
|
"Connection rejected, device %4.4X unavailable",
|
|
devnum);
|
|
|
|
TNSDEBUG1( "%s\n", rejmsg);
|
|
|
|
/* Send connection rejection message to client */
|
|
if (class == 'D')
|
|
{
|
|
len = sprintf (buf,
|
|
"\xF5\x40\x11\x40\x40\x1D\x60%s"
|
|
"\x11\xC1\x50\x1D\x60%s"
|
|
"\x11\xC2\x60\x1D\x60%s",
|
|
translate_to_ebcdic(conmsg),
|
|
translate_to_ebcdic(hostmsg),
|
|
translate_to_ebcdic(rejmsg));
|
|
buf[len++] = IAC;
|
|
buf[len++] = EOR_MARK;
|
|
}
|
|
else
|
|
{
|
|
len = sprintf (buf, "%s\r\n%s\r\n%s\r\n", conmsg, hostmsg, rejmsg);
|
|
}
|
|
rc = send_packet (csock, buf, len, "CONNECTION RESPONSE");
|
|
|
|
/* Close the connection and terminate the thread */
|
|
sleep (5);
|
|
close (csock);
|
|
return NULL;
|
|
}
|
|
|
|
logmsg (_("HHC604I Client %s connected to %4.4X device %4.4X\n"),
|
|
clientip, dev->devtype, dev->devnum);
|
|
|
|
/* Send connection message to client */
|
|
if (class == 'D')
|
|
{
|
|
len = sprintf (buf,
|
|
"\xF5\x40\x11\x40\x40\x1D\x60%s"
|
|
"\x11\xC1\x50\x1D\x60%s",
|
|
translate_to_ebcdic(conmsg),
|
|
translate_to_ebcdic(hostmsg));
|
|
buf[len++] = IAC;
|
|
buf[len++] = EOR_MARK;
|
|
}
|
|
else
|
|
{
|
|
len = sprintf (buf, "%s\r\n%s\r\n", conmsg, hostmsg);
|
|
}
|
|
rc = send_packet (csock, buf, len, "CONNECTION RESPONSE");
|
|
|
|
/* Raise attention interrupt for the device */
|
|
rc = device_attention (dev, CSW_ATTN);
|
|
|
|
/* Signal connection thread to redrive its select loop */
|
|
signal_thread (sysblk.cnsltid, SIGUSR2);
|
|
|
|
return NULL;
|
|
|
|
} /* end function connect_client */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* CONSOLE CONNECTION AND ATTENTION HANDLER THREAD */
|
|
/*-------------------------------------------------------------------*/
|
|
static void *
|
|
console_connection_handler (void *arg)
|
|
{
|
|
int rc = 0; /* Return code */
|
|
int lsock; /* Socket for listening */
|
|
int csock; /* Socket for conversation */
|
|
struct sockaddr_in server; /* Server address structure */
|
|
fd_set readset; /* Read bit map for select */
|
|
int maxfd; /* Highest fd for select */
|
|
int optval; /* Argument for setsockopt */
|
|
TID tidneg; /* Negotiation thread id */
|
|
DEVBLK *dev; /* -> Device block */
|
|
BYTE unitstat; /* Status after receive data */
|
|
|
|
/* Display thread started message on control panel */
|
|
logmsg (_("HHC600I Console connection thread started: "
|
|
"tid="TIDPAT", pid=%d\n"),
|
|
thread_id(), getpid());
|
|
|
|
/* Get information about this system */
|
|
uname (&hostinfo);
|
|
|
|
/* Obtain a socket */
|
|
lsock = socket (AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (lsock < 0)
|
|
{
|
|
TNSERROR("socket: %s\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
/* Allow previous instance of socket to be reused */
|
|
optval = 1;
|
|
setsockopt (lsock, SOL_SOCKET, SO_REUSEADDR,
|
|
&optval, sizeof(optval));
|
|
|
|
/* Prepare the sockaddr structure for the bind */
|
|
memset (&server, 0, sizeof(server));
|
|
server.sin_family = AF_INET;
|
|
server.sin_addr.s_addr = INADDR_ANY;
|
|
server.sin_port = sysblk.cnslport;
|
|
server.sin_port = htons(server.sin_port);
|
|
|
|
/* Attempt to bind the socket to the port */
|
|
while (sysblk.cnslcnt)
|
|
{
|
|
rc = bind (lsock, (struct sockaddr *)&server, sizeof(server));
|
|
|
|
if (rc == 0 || errno != EADDRINUSE) break;
|
|
|
|
logmsg (_("HHC601I Waiting for port %u to become free\n"),
|
|
sysblk.cnslport);
|
|
sleep(10);
|
|
} /* end while */
|
|
|
|
if (rc != 0)
|
|
{
|
|
TNSERROR("bind: %s\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
/* Put the socket into listening state */
|
|
rc = listen (lsock, 10);
|
|
|
|
if (rc < 0)
|
|
{
|
|
TNSERROR("listen: %s\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
logmsg (_("HHC602I Waiting for console connection on port %u\n"),
|
|
sysblk.cnslport);
|
|
|
|
/* Handle connection requests and attention interrupts */
|
|
while (sysblk.cnslcnt) {
|
|
|
|
/* Initialize the select parameters */
|
|
maxfd = lsock;
|
|
FD_ZERO (&readset);
|
|
FD_SET (lsock, &readset);
|
|
|
|
/* Include the socket for each connected console */
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
{
|
|
if (dev->console
|
|
&& dev->connected
|
|
&& (dev->busy == 0 || (dev->scsw.flag3 & SCSW3_AC_SUSP))
|
|
&& dev->pending == 0
|
|
&& (dev->pmcw.flag5 & PMCW5_V)
|
|
// NOT S/370 && (dev->pmcw.flag5 & PMCW5_E)
|
|
&& (dev->scsw.flag3 & SCSW3_SC_PEND) == 0)
|
|
{
|
|
FD_SET (dev->fd, &readset);
|
|
if (dev->fd > maxfd) maxfd = dev->fd;
|
|
}
|
|
} /* end for(dev) */
|
|
|
|
/* Wait for a file descriptor to become ready */
|
|
#ifdef WIN32
|
|
{
|
|
struct timeval tv={0,500000}; /* half a second */
|
|
rc = select ( maxfd+1, &readset, NULL, NULL, &tv );
|
|
}
|
|
#else /* WIN32 */
|
|
rc = select ( maxfd+1, &readset, NULL, NULL, NULL );
|
|
#endif /* WIN32 */
|
|
|
|
if (rc == 0) continue;
|
|
|
|
if (rc < 0 )
|
|
{
|
|
if (errno == EINTR) continue;
|
|
TNSERROR("select: %s\n", strerror(errno));
|
|
break;
|
|
}
|
|
|
|
/* If a client connection request has arrived then accept it */
|
|
if (FD_ISSET(lsock, &readset))
|
|
{
|
|
/* Accept a connection and create conversation socket */
|
|
csock = accept (lsock, NULL, NULL);
|
|
|
|
if (csock < 0)
|
|
{
|
|
TNSERROR("accept: %s\n", strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
/* Create a thread to complete the client connection */
|
|
if ( create_thread (&tidneg, &sysblk.detattr,
|
|
connect_client, &csock) )
|
|
{
|
|
TNSERROR("connect_client create_thread: %s\n",
|
|
strerror(errno));
|
|
close (csock);
|
|
}
|
|
|
|
} /* end if(lsock) */
|
|
|
|
/* Check if any connected client has data ready to send */
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
{
|
|
/* Obtain the device lock */
|
|
obtain_lock (&dev->lock);
|
|
|
|
/* Test for connected console with data available */
|
|
if (dev->console
|
|
&& dev->connected
|
|
&& FD_ISSET (dev->fd, &readset)
|
|
&& (dev->busy == 0 || (dev->scsw.flag3 & SCSW3_AC_SUSP))
|
|
&& dev->pending == 0
|
|
&& (dev->pmcw.flag5 & PMCW5_V)
|
|
// NOT S/370 && (dev->pmcw.flag5 & PMCW5_E)
|
|
&& (dev->scsw.flag3 & SCSW3_SC_PEND) == 0)
|
|
{
|
|
/* Receive console input data from the client */
|
|
if (dev->devtype == 0x3270)
|
|
unitstat = recv_3270_data (dev);
|
|
else
|
|
unitstat = recv_1052_data (dev);
|
|
|
|
/* Nothing more to do if incomplete record received */
|
|
if (unitstat == 0)
|
|
{
|
|
release_lock (&dev->lock);
|
|
continue;
|
|
}
|
|
|
|
/* Close the connection if an error occurred */
|
|
if (unitstat & CSW_UC)
|
|
{
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
dev->connected = 0;
|
|
}
|
|
|
|
/* Indicate that data is available at the device */
|
|
if(dev->rlen3270)
|
|
dev->readpending = 1;
|
|
|
|
/* Release the device lock */
|
|
release_lock (&dev->lock);
|
|
|
|
/* Raise attention interrupt for the device */
|
|
rc = device_attention (dev, unitstat);
|
|
|
|
/* Trace the attention request */
|
|
TNSDEBUG2("%4.4X attention request %s\n",
|
|
dev->devnum,
|
|
(rc == 0 ? "raised" : "rejected"));
|
|
|
|
continue;
|
|
} /* end if(data available) */
|
|
|
|
/* Release the device lock */
|
|
release_lock (&dev->lock);
|
|
|
|
} /* end for(dev) */
|
|
|
|
} /* end while */
|
|
|
|
/* Close the listening socket */
|
|
close (lsock);
|
|
|
|
logmsg (_("HHC609I Console connection thread terminated\n"));
|
|
|
|
return NULL;
|
|
|
|
} /* end function console_connection_handler */
|
|
|
|
|
|
static int
|
|
console_initialise()
|
|
{
|
|
if(!(sysblk.cnslcnt++))
|
|
{
|
|
if ( create_thread (&sysblk.cnsltid, &sysblk.detattr,
|
|
console_connection_handler, NULL) )
|
|
{
|
|
logmsg (_("HHC135I Cannot create console thread: %s\n"),
|
|
strerror(errno));
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
console_remove(DEVBLK *dev)
|
|
{
|
|
if(dev->fd > 2)
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
|
|
/* Reset device dependent flags */
|
|
dev->connected = 0;
|
|
dev->console = 0;
|
|
|
|
if(!sysblk.cnslcnt--)
|
|
logmsg(_("console_remove() error\n"));
|
|
|
|
signal_thread (sysblk.cnsltid, SIGUSR2);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* INITIALIZE THE 3270 DEVICE HANDLER */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
loc3270_init_handler ( DEVBLK *dev, int argc, BYTE *argv[] )
|
|
{
|
|
/* Indicate that this is a console device */
|
|
dev->console = 1;
|
|
|
|
/* Reset device dependent flags */
|
|
dev->connected = 0;
|
|
|
|
/* Set number of sense bytes */
|
|
dev->numsense = 1;
|
|
|
|
/* Set the size of the device buffer */
|
|
dev->bufsize = BUFLEN_3270;
|
|
|
|
/* Initialize the device identifier bytes */
|
|
dev->devid[0] = 0xFF;
|
|
dev->devid[1] = 0x32; /* Control unit type is 3274-1D */
|
|
dev->devid[2] = 0x74;
|
|
dev->devid[3] = 0x1D;
|
|
dev->devid[4] = 0x32; /* Device type is 3278-2 */
|
|
dev->devid[5] = 0x78;
|
|
dev->devid[6] = 0x02;
|
|
dev->numdevid = 7;
|
|
|
|
return console_initialise();
|
|
} /* end function loc3270_init_handler */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* QUERY THE 3270 DEVICE DEFINITION */
|
|
/*-------------------------------------------------------------------*/
|
|
static void
|
|
loc3270_query_device (DEVBLK *dev, BYTE **class,
|
|
int buflen, BYTE *buffer)
|
|
{
|
|
|
|
*class = "DSP";
|
|
|
|
if (dev->connected)
|
|
snprintf (buffer, buflen, "%s",
|
|
(BYTE*)inet_ntoa(dev->ipaddr));
|
|
else
|
|
buffer[0] = '\0';
|
|
|
|
} /* end function loc3270_query_device */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* CLOSE THE 3270 DEVICE HANDLER */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
loc3270_close_device ( DEVBLK *dev )
|
|
{
|
|
console_remove(dev);
|
|
|
|
return 0;
|
|
} /* end function loc3270_close_device */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* INITIALIZE THE 1052/3215 DEVICE HANDLER */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
constty_init_handler ( DEVBLK *dev, int argc, BYTE *argv[] )
|
|
{
|
|
/* Indicate that this is a console device */
|
|
dev->console = 1;
|
|
|
|
/* Set number of sense bytes */
|
|
dev->numsense = 1;
|
|
|
|
/* Initialize device dependent fields */
|
|
dev->keybdrem = 0;
|
|
|
|
/* Set length of print buffer */
|
|
dev->bufsize = BUFLEN_1052;
|
|
|
|
/* Initialize the device identifier bytes */
|
|
dev->devid[0] = 0xFF;
|
|
dev->devid[1] = dev->devtype >> 8;
|
|
dev->devid[2] = dev->devtype & 0xFF;
|
|
dev->devid[3] = 0x00;
|
|
dev->devid[4] = dev->devtype >> 8;
|
|
dev->devid[5] = dev->devtype & 0xFF;
|
|
dev->devid[6] = 0x00;
|
|
dev->numdevid = 7;
|
|
|
|
return console_initialise();
|
|
|
|
} /* end function constty_init_handler */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* QUERY THE 1052/3215 DEVICE DEFINITION */
|
|
/*-------------------------------------------------------------------*/
|
|
static void
|
|
constty_query_device (DEVBLK *dev, BYTE **class,
|
|
int buflen, BYTE *buffer)
|
|
{
|
|
|
|
*class = "CON";
|
|
|
|
if (dev->connected)
|
|
snprintf (buffer, buflen, "%s",
|
|
(BYTE*)inet_ntoa(dev->ipaddr));
|
|
else
|
|
buffer[0] = '\0';
|
|
|
|
} /* end function constty_query_device */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* CLOSE THE 1052/3215 DEVICE HANDLER */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
constty_close_device ( DEVBLK *dev )
|
|
{
|
|
console_remove(dev);
|
|
|
|
return 0;
|
|
} /* end function constty_close_device */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO ADVANCE TO NEXT CHAR OR ORDER IN A 3270 DATA STREAM */
|
|
/* Input: */
|
|
/* buf Buffer containing 3270 data stream */
|
|
/* off Offset in buffer of current character or order */
|
|
/* pos Position on screen of current character or order */
|
|
/* Output: */
|
|
/* off Offset in buffer of next character or order */
|
|
/* pos Position on screen of next character or order */
|
|
/*-------------------------------------------------------------------*/
|
|
static void
|
|
next_3270_pos (BYTE *buf, int *off, int *pos)
|
|
{
|
|
int i;
|
|
|
|
/* Copy the offset and advance the offset by 1 byte */
|
|
i = (*off)++;
|
|
|
|
/* Advance the offset past the argument bytes and set position */
|
|
switch (buf[i]) {
|
|
|
|
/* The Repeat to Address order has 3 argument bytes (or in case
|
|
of a Graphics Escape 4 bytes) and sets the screen position */
|
|
case O3270_RA:
|
|
|
|
*off += (buf[i+3] == O3270_GE) ? 4 : 3;
|
|
if ((buf[i+1] & 0xC0) == 0x00)
|
|
*pos = (buf[i+1] << 8) | buf[i+2];
|
|
else
|
|
*pos = ((buf[i+1] & 0x3F) << 6)
|
|
| (buf[i+2] & 0x3F);
|
|
break;
|
|
|
|
/* The Start Field Extended and Modify Field orders have
|
|
a count byte followed by a variable number of type-
|
|
attribute pairs, and advance the screen position by 1 */
|
|
case O3270_SFE:
|
|
case O3270_MF:
|
|
|
|
*off += (1 + 2*buf[i+1]);
|
|
(*pos)++;
|
|
break;
|
|
|
|
/* The Set Buffer Address and Erase Unprotected to Address
|
|
orders have 2 argument bytes and set the screen position */
|
|
case O3270_SBA:
|
|
case O3270_EUA:
|
|
|
|
*off += 2;
|
|
if ((buf[i+1] & 0xC0) == 0x00)
|
|
*pos = (buf[i+1] << 8) | buf[i+2];
|
|
else
|
|
*pos = ((buf[i+1] & 0x3F) << 6)
|
|
| (buf[i+2] & 0x3F);
|
|
break;
|
|
|
|
/* The Set Attribute order has 2 argument bytes and
|
|
does not change the screen position */
|
|
case O3270_SA:
|
|
|
|
*off += 2;
|
|
break;
|
|
|
|
/* Insert Cursor and Program Tab have no argument
|
|
bytes and do not change the screen position */
|
|
case O3270_IC:
|
|
case O3270_PT:
|
|
|
|
break;
|
|
|
|
/* The Start Field and Graphics Escape orders have one
|
|
argument byte, and advance the screen position by 1 */
|
|
case O3270_SF:
|
|
case O3270_GE:
|
|
|
|
(*off)++;
|
|
(*pos)++;
|
|
break;
|
|
|
|
/* All other characters advance the screen position by 1 */
|
|
default:
|
|
|
|
(*pos)++;
|
|
break;
|
|
|
|
} /* end switch */
|
|
|
|
} /* end function next_3270_pos */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO FIND A GIVEN SCREEN POSITION IN A 3270 READ BUFFER */
|
|
/* Input: */
|
|
/* buf Buffer containing an inbound 3270 data stream */
|
|
/* size Number of bytes in buffer */
|
|
/* pos Screen position whose offset in buffer is desired */
|
|
/* Return value: */
|
|
/* Offset in buffer of the character or order corresponding to */
|
|
/* the given screen position, or zero if position not found. */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
find_buffer_pos (BYTE *buf, int size, int pos)
|
|
{
|
|
int wpos; /* Current screen position */
|
|
int woff; /* Current offset in buffer */
|
|
|
|
/* Screen position 0 is at offset 3 in the device buffer,
|
|
following the AID and cursor address bytes */
|
|
wpos = 0;
|
|
woff = 3;
|
|
|
|
while (woff < size)
|
|
{
|
|
/* Exit if desired screen position has been reached */
|
|
if (wpos >= pos)
|
|
{
|
|
// logmsg (_("console: Pos %4.4X reached at %4.4X\n"),
|
|
// wpos, woff);
|
|
|
|
#ifdef FIX_QWS_BUG_FOR_MCS_CONSOLES
|
|
/* There is a bug in QWS3270 when used to emulate an
|
|
MCS console with EAB. At position 1680 the Read
|
|
Buffer contains two 6-byte SFE orders (12 bytes)
|
|
preceding the entry area, whereas MCS expects the
|
|
entry area to start 4 bytes after screen position
|
|
1680 in the buffer. The bypass is to add 8 to the
|
|
calculated buffer offset if this appears to be an
|
|
MCS console read buffer command */
|
|
if (pos == 0x0690 && buf[woff] == O3270_SFE
|
|
&& buf[woff+6] == O3270_SFE)
|
|
{
|
|
woff += 8;
|
|
// logmsg (_("console: Pos %4.4X adjusted to %4.4X\n"),
|
|
// wpos, woff);
|
|
}
|
|
#endif /*FIX_QWS_BUG_FOR_MCS_CONSOLES*/
|
|
|
|
return woff;
|
|
}
|
|
|
|
/* Process next character or order, update screen position */
|
|
next_3270_pos (buf, &woff, &wpos);
|
|
|
|
} /* end while */
|
|
|
|
/* Return offset zero if the position cannot be determined */
|
|
return 0;
|
|
|
|
} /* end function find_buffer_pos */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SUBROUTINE TO UPDATE THE CURRENT SCREEN POSITION */
|
|
/* Input: */
|
|
/* pos Current screen position */
|
|
/* buf Pointer to the byte in the 3270 data stream */
|
|
/* corresponding to the current screen position */
|
|
/* size Number of bytes remaining in buffer */
|
|
/* Output: */
|
|
/* pos Updated screen position after end of buffer */
|
|
/*-------------------------------------------------------------------*/
|
|
static void
|
|
get_screen_pos (int *pos, BYTE *buf, int size)
|
|
{
|
|
int woff = 0; /* Current offset in buffer */
|
|
|
|
while (woff < size)
|
|
{
|
|
/* Process next character or order, update screen position */
|
|
next_3270_pos (buf, &woff, pos);
|
|
|
|
} /* end while */
|
|
|
|
} /* end function get_screen_pos */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* EXECUTE A 3270 CHANNEL COMMAND WORD */
|
|
/*-------------------------------------------------------------------*/
|
|
static void
|
|
loc3270_execute_ccw ( DEVBLK *dev, BYTE code, BYTE flags,
|
|
BYTE chained, U16 count, BYTE prevcode, int ccwseq,
|
|
BYTE *iobuf, BYTE *more, BYTE *unitstat, U16 *residual )
|
|
{
|
|
int rc; /* Return code */
|
|
int num; /* Number of bytes to copy */
|
|
int len; /* Data length */
|
|
int aid; /* First read: AID present */
|
|
int off; /* Offset in device buffer */
|
|
BYTE cmd; /* tn3270 command code */
|
|
BYTE buf[BUFLEN_3270]; /* tn3270 write buffer */
|
|
|
|
/* Clear the current screen position at start of CCW chain */
|
|
if (!chained)
|
|
dev->pos3270 = 0;
|
|
|
|
/* Unit check with intervention required if no client connected */
|
|
if (!dev->connected && !IS_CCW_SENSE(code))
|
|
{
|
|
dev->sense[0] = SENSE_IR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return;
|
|
}
|
|
|
|
/* Process depending on CCW opcode */
|
|
switch (code) {
|
|
|
|
case L3270_NOP:
|
|
/*---------------------------------------------------------------*/
|
|
/* CONTROL NO-OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
/* Reset the buffer address */
|
|
dev->pos3270 = 0;
|
|
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case L3270_SELRM:
|
|
case L3270_SELRB:
|
|
case L3270_SELRMP:
|
|
case L3270_SELRBP:
|
|
case L3270_SELWRT:
|
|
/*---------------------------------------------------------------*/
|
|
/* SELECT */
|
|
/*---------------------------------------------------------------*/
|
|
/* Reset the buffer address */
|
|
dev->pos3270 = 0;
|
|
|
|
*residual = 0;
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case L3270_EAU:
|
|
/*---------------------------------------------------------------*/
|
|
/* ERASE ALL UNPROTECTED */
|
|
/*---------------------------------------------------------------*/
|
|
dev->pos3270 = 0;
|
|
cmd = R3270_EAU;
|
|
goto write;
|
|
|
|
case L3270_WRT:
|
|
/*---------------------------------------------------------------*/
|
|
/* WRITE */
|
|
/*---------------------------------------------------------------*/
|
|
cmd = R3270_WRT;
|
|
goto write;
|
|
|
|
case L3270_EW:
|
|
/*---------------------------------------------------------------*/
|
|
/* ERASE/WRITE */
|
|
/*---------------------------------------------------------------*/
|
|
dev->pos3270 = 0;
|
|
cmd = R3270_EW;
|
|
goto write;
|
|
|
|
case L3270_EWA:
|
|
/*---------------------------------------------------------------*/
|
|
/* ERASE/WRITE ALTERNATE */
|
|
/*---------------------------------------------------------------*/
|
|
dev->pos3270 = 0;
|
|
cmd = R3270_EWA;
|
|
goto write;
|
|
|
|
case L3270_WSF:
|
|
/*---------------------------------------------------------------*/
|
|
/* WRITE STRUCTURED FIELD */
|
|
/*---------------------------------------------------------------*/
|
|
/* Process WSF command if device has extended attributes */
|
|
if (dev->eab3270)
|
|
{
|
|
dev->pos3270 = 0;
|
|
cmd = R3270_WSF;
|
|
goto write;
|
|
}
|
|
|
|
/* Operation check, device does not have extended attributes */
|
|
dev->sense[0] = SENSE_OC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
|
|
write:
|
|
/*---------------------------------------------------------------*/
|
|
/* All write commands, and the EAU control command, come here */
|
|
/*---------------------------------------------------------------*/
|
|
/* Initialize the data length */
|
|
len = 0;
|
|
|
|
/* Calculate number of bytes to move and residual byte count */
|
|
num = sizeof(buf) / 2;
|
|
num = (count < num) ? count : num;
|
|
*residual = count - num;
|
|
|
|
/* Move the 3270 command code to the first byte of the buffer
|
|
unless data-chained from previous CCW */
|
|
if ((chained & CCW_FLAGS_CD) == 0)
|
|
{
|
|
buf[len++] = cmd;
|
|
/* If this is a chained write then we start at the
|
|
current buffer address rather then the cursor address.
|
|
If the first action the datastream takes is not a
|
|
positioning action then insert a SBA to position to
|
|
the current buffer address */
|
|
if(chained
|
|
&& cmd == R3270_WRT
|
|
&& dev->pos3270 != 0
|
|
&& iobuf[1] != O3270_SBA
|
|
&& iobuf[1] != O3270_RA
|
|
&& iobuf[1] != O3270_EUA)
|
|
{
|
|
/* Copy the write control character and ajust buffer */
|
|
buf[len++] = *iobuf++; num--;
|
|
/* Insert the SBA order */
|
|
buf[len++] = O3270_SBA;
|
|
if(dev->pos3270 < 4096)
|
|
{
|
|
buf[len++] = sba_code[dev->pos3270 >> 6];
|
|
buf[len++] = sba_code[dev->pos3270 & 0x3F];
|
|
}
|
|
else
|
|
{
|
|
buf[len++] = dev->pos3270 >> 8;
|
|
buf[len++] = dev->pos3270 & 0xFF;
|
|
}
|
|
} /* if(iobuf[0] != SBA, RA or EUA) */
|
|
|
|
/* Save the screen position at completion of the write.
|
|
This is necessary in case a Read Buffer command is chained
|
|
from another write or read, this does not apply for the
|
|
write structured field command */
|
|
if(cmd != R3270_WSF)
|
|
get_screen_pos (&dev->pos3270, iobuf+1, num-1);
|
|
|
|
} /* if(!data_chained) */
|
|
else /* if(data_chained) */
|
|
if(cmd != R3270_WSF)
|
|
get_screen_pos (&dev->pos3270, iobuf, num);
|
|
|
|
/* Copy data from channel buffer to device buffer */
|
|
memcpy (buf + len, iobuf, num);
|
|
len += num;
|
|
|
|
/* Double up any IAC bytes in the data */
|
|
len = double_up_iac (buf, len);
|
|
|
|
/* Append telnet EOR marker at end of data */
|
|
if ((flags & CCW_FLAGS_CD) == 0) {
|
|
buf[len++] = IAC;
|
|
buf[len++] = EOR_MARK;
|
|
}
|
|
|
|
/* Send the data to the client */
|
|
rc = send_packet(dev->fd, buf, len, "3270 data");
|
|
if (rc < 0)
|
|
{
|
|
dev->sense[0] = SENSE_DC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Return normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case L3270_RB:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ BUFFER */
|
|
/*---------------------------------------------------------------*/
|
|
/* Obtain the device lock */
|
|
obtain_lock (&dev->lock);
|
|
|
|
/* AID is only present during the first read */
|
|
aid = dev->readpending != 2;
|
|
|
|
/* Receive buffer data from client if not data chained */
|
|
if ((chained & CCW_FLAGS_CD) == 0)
|
|
{
|
|
/* Send read buffer command to client and await response */
|
|
rc = solicit_3270_data (dev, R3270_RB);
|
|
if (rc & CSW_UC)
|
|
{
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
release_lock (&dev->lock);
|
|
break;
|
|
}
|
|
|
|
/* Set AID in buffer flag */
|
|
aid = 1;
|
|
|
|
/* Save the AID of the current inbound transmission */
|
|
dev->aid3270 = dev->buf[0];
|
|
if(dev->pos3270 != 0 && dev->aid3270 != SF3270_AID)
|
|
{
|
|
/* Find offset in buffer of current screen position */
|
|
off = find_buffer_pos (dev->buf, dev->rlen3270,
|
|
dev->pos3270);
|
|
|
|
/* Shift out unwanted characters from buffer */
|
|
num = (dev->rlen3270 > off ? dev->rlen3270 - off : 0);
|
|
memmove (dev->buf + 3, dev->buf + off, num);
|
|
dev->rlen3270 = 3 + num;
|
|
}
|
|
|
|
} /* end if(!CCW_FLAGS_CD) */
|
|
|
|
/* Calculate number of bytes to move and residual byte count */
|
|
len = dev->rlen3270;
|
|
num = (count < len) ? count : len;
|
|
*residual = count - num;
|
|
if (count < len) *more = 1;
|
|
|
|
/* Save the screen position at completion of the read.
|
|
This is necessary in case a Read Buffer command is chained
|
|
from another write or read. */
|
|
if(dev->aid3270 != SF3270_AID)
|
|
{
|
|
if(aid)
|
|
get_screen_pos(&dev->pos3270, dev->buf+3, num-3);
|
|
else
|
|
get_screen_pos(&dev->pos3270, dev->buf, num);
|
|
}
|
|
|
|
/* Indicate that the AID bytes have been skipped */
|
|
if(dev->readpending == 1)
|
|
dev->readpending = 2;
|
|
|
|
/* Copy data from device buffer to channel buffer */
|
|
memcpy (iobuf, dev->buf, num);
|
|
|
|
/* If data chaining is specified, save remaining data */
|
|
if ((flags & CCW_FLAGS_CD) && len > count)
|
|
{
|
|
memmove (dev->buf, dev->buf + count, len - count);
|
|
dev->rlen3270 = len - count;
|
|
}
|
|
else
|
|
{
|
|
dev->rlen3270 = 0;
|
|
dev->readpending = 0;
|
|
}
|
|
|
|
/* Return normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
|
|
/* Release the device lock */
|
|
release_lock (&dev->lock);
|
|
break;
|
|
|
|
case L3270_RM:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ MODIFIED */
|
|
/*---------------------------------------------------------------*/
|
|
/* Obtain the device lock */
|
|
obtain_lock (&dev->lock);
|
|
|
|
/* AID is only present during the first read */
|
|
aid = dev->readpending != 2;
|
|
|
|
/* If not data chained from previous Read Modified CCW,
|
|
and if the connection thread has not already accumulated
|
|
a complete Read Modified record in the inbound buffer,
|
|
then solicit a Read Modified operation at the client */
|
|
if ((chained & CCW_FLAGS_CD) == 0
|
|
&& !dev->readpending)
|
|
{
|
|
/* Send read modified command to client, await response */
|
|
rc = solicit_3270_data (dev, R3270_RM);
|
|
if (rc & CSW_UC)
|
|
{
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
release_lock (&dev->lock);
|
|
break;
|
|
}
|
|
|
|
dev->aid3270 = dev->buf[0];
|
|
if(dev->pos3270 != 0 && dev->aid3270 != SF3270_AID)
|
|
{
|
|
/* Find offset in buffer of current screen position */
|
|
off = find_buffer_pos (dev->buf, dev->rlen3270,
|
|
dev->pos3270);
|
|
|
|
/* Shift out unwanted characters from buffer */
|
|
num = (dev->rlen3270 > off ? dev->rlen3270 - off : 0);
|
|
memmove (dev->buf + 3, dev->buf + off, num);
|
|
dev->rlen3270 = 3 + num;
|
|
}
|
|
|
|
} /* end if(!CCW_FLAGS_CD) */
|
|
|
|
/* Calculate number of bytes to move and residual byte count */
|
|
len = dev->rlen3270;
|
|
num = (count < len) ? count : len;
|
|
*residual = count - num;
|
|
if (count < len) *more = 1;
|
|
|
|
/* Save the screen position at completion of the read.
|
|
This is necessary in case a Read Buffer command is chained
|
|
from another write or read. */
|
|
if(dev->aid3270 != SF3270_AID)
|
|
{
|
|
if(aid)
|
|
get_screen_pos(&dev->pos3270, dev->buf+3, num-3);
|
|
else
|
|
get_screen_pos(&dev->pos3270, dev->buf, num);
|
|
}
|
|
|
|
/* Indicate that the AID bytes have been skipped */
|
|
if(dev->readpending == 1)
|
|
dev->readpending = 2;
|
|
|
|
/* Copy data from device buffer to channel buffer */
|
|
memcpy (iobuf, dev->buf, num);
|
|
|
|
/* If data chaining is specified, save remaining data */
|
|
if ((flags & CCW_FLAGS_CD) && len > count)
|
|
{
|
|
memmove (dev->buf, dev->buf + count, len - count);
|
|
dev->rlen3270 = len - count;
|
|
}
|
|
else
|
|
{
|
|
dev->rlen3270 = 0;
|
|
dev->readpending = 0;
|
|
}
|
|
|
|
/* Set normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
|
|
/* Release the device lock */
|
|
release_lock (&dev->lock);
|
|
|
|
/* Signal connection thread to redrive its select loop */
|
|
signal_thread (sysblk.cnsltid, SIGUSR2);
|
|
|
|
break;
|
|
|
|
case L3270_SENSE:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numsense) ? count : dev->numsense;
|
|
*residual = count - num;
|
|
if (count < dev->numsense) *more = 1;
|
|
|
|
/* Copy device sense bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->sense, num);
|
|
|
|
/* Clear the device sense bytes */
|
|
memset (dev->sense, 0, sizeof(dev->sense));
|
|
|
|
/* Reset the buffer address */
|
|
dev->pos3270 = 0;
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case L3270_SENSEID:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE ID */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numdevid) ? count : dev->numdevid;
|
|
*residual = count - num;
|
|
if (count < dev->numdevid) *more = 1;
|
|
|
|
/* Copy device identifier bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->devid, num);
|
|
|
|
/* Reset the buffer address */
|
|
dev->pos3270 = 0;
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
default:
|
|
/*---------------------------------------------------------------*/
|
|
/* INVALID OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
/* Set command reject sense byte, and unit check status */
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
|
|
} /* end switch(code) */
|
|
|
|
} /* end function loc3270_execute_ccw */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* EXECUTE A 1052/3215 CHANNEL COMMAND WORD */
|
|
/*-------------------------------------------------------------------*/
|
|
static void
|
|
constty_execute_ccw ( DEVBLK *dev, BYTE code, BYTE flags,
|
|
BYTE chained, U16 count, BYTE prevcode, int ccwseq,
|
|
BYTE *iobuf, BYTE *more, BYTE *unitstat, U16 *residual )
|
|
{
|
|
int rc; /* Return code */
|
|
int len; /* Length of data */
|
|
int num; /* Number of bytes to move */
|
|
BYTE c; /* Print character */
|
|
BYTE stat; /* Unit status */
|
|
|
|
/* Unit check with intervention required if no client connected */
|
|
if (dev->connected == 0 && !IS_CCW_SENSE(code))
|
|
{
|
|
dev->sense[0] = SENSE_IR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return;
|
|
}
|
|
|
|
/* Process depending on CCW opcode */
|
|
switch (code) {
|
|
|
|
case 0x01:
|
|
/*---------------------------------------------------------------*/
|
|
/* WRITE NO CARRIER RETURN */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
case 0x09:
|
|
/*---------------------------------------------------------------*/
|
|
/* WRITE AUTO CARRIER RETURN */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* Calculate number of bytes to write and set residual count */
|
|
num = (count < BUFLEN_1052) ? count : BUFLEN_1052;
|
|
*residual = count - num;
|
|
|
|
/* Translate data in channel buffer to ASCII */
|
|
for (len = 0; len < num; len++)
|
|
{
|
|
c = guest_to_host(iobuf[len]);
|
|
if (!isprint(c) && c != 0x0a && c != 0x0d) c = SPACE;
|
|
iobuf[len] = c;
|
|
} /* end for(len) */
|
|
|
|
/* Perform end of record processing if not data-chaining */
|
|
if ((flags & CCW_FLAGS_CD) == 0)
|
|
{
|
|
/* Append carriage return and newline if required */
|
|
if (code == 0x09)
|
|
{
|
|
iobuf[len++] = '\r';
|
|
iobuf[len++] = '\n';
|
|
}
|
|
|
|
} /* end if(!data-chaining) */
|
|
|
|
/* Send the data to the client */
|
|
rc = send_packet (dev->fd, iobuf, len, NULL);
|
|
if (rc < 0)
|
|
{
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Return normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x03:
|
|
/*---------------------------------------------------------------*/
|
|
/* CONTROL NO-OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x0A:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ INQUIRY */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* Solicit console input if no data in the device buffer */
|
|
if (dev->keybdrem == 0)
|
|
{
|
|
/* Display prompting message on console */
|
|
len = sprintf (dev->buf,
|
|
"HHC901I Enter input for console device %4.4X\r\n",
|
|
dev->devnum);
|
|
rc = send_packet (dev->fd, dev->buf, len, NULL);
|
|
if (rc < 0)
|
|
{
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Accumulate client input data into device buffer */
|
|
while (1) {
|
|
|
|
/* Receive client data and increment dev->keybdrem */
|
|
stat = recv_1052_data (dev);
|
|
|
|
/* Exit if error or end of line */
|
|
if (stat != 0)
|
|
break;
|
|
|
|
} /* end while */
|
|
|
|
/* Exit if error status */
|
|
if (stat != CSW_ATTN)
|
|
{
|
|
*unitstat = (CSW_CE | CSW_DE) | (stat & ~CSW_ATTN);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
/* Calculate number of bytes to move and residual byte count */
|
|
len = dev->keybdrem;
|
|
num = (count < len) ? count : len;
|
|
*residual = count - num;
|
|
if (count < len) *more = 1;
|
|
|
|
/* Copy data from device buffer to channel buffer */
|
|
memcpy (iobuf, dev->buf, num);
|
|
|
|
/* If data chaining is specified, save remaining data */
|
|
if ((flags & CCW_FLAGS_CD) && len > count)
|
|
{
|
|
memmove (dev->buf, dev->buf + count, len - count);
|
|
dev->keybdrem = len - count;
|
|
}
|
|
else
|
|
{
|
|
dev->keybdrem = 0;
|
|
}
|
|
|
|
/* Return normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x0B:
|
|
/*---------------------------------------------------------------*/
|
|
/* AUDIBLE ALARM */
|
|
/*---------------------------------------------------------------*/
|
|
rc = send_packet (dev->fd, "\a", 1, NULL);
|
|
*residual = 0;
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x04:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numsense) ? count : dev->numsense;
|
|
*residual = count - num;
|
|
if (count < dev->numsense) *more = 1;
|
|
|
|
/* Copy device sense bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->sense, num);
|
|
|
|
/* Clear the device sense bytes */
|
|
memset (dev->sense, 0, sizeof(dev->sense));
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0xE4:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE ID */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numdevid) ? count : dev->numdevid;
|
|
*residual = count - num;
|
|
if (count < dev->numdevid) *more = 1;
|
|
|
|
/* Copy device identifier bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->devid, num);
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
default:
|
|
/*---------------------------------------------------------------*/
|
|
/* INVALID OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
/* Set command reject sense byte, and unit check status */
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
|
|
} /* end switch(code) */
|
|
|
|
} /* end function constty_execute_ccw */
|
|
|
|
|
|
DEVHND constty_device_hndinfo = {
|
|
&constty_init_handler,
|
|
&constty_execute_ccw,
|
|
&constty_close_device,
|
|
&constty_query_device,
|
|
NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
|
|
DEVHND loc3270_device_hndinfo = {
|
|
&loc3270_init_handler,
|
|
&loc3270_execute_ccw,
|
|
&loc3270_close_device,
|
|
&loc3270_query_device,
|
|
NULL, NULL, NULL, NULL
|
|
};
|