summaryrefslogtreecommitdiffstats
path: root/chardev
diff options
context:
space:
mode:
authorStefan Hajnoczi2017-05-05 17:56:23 +0200
committerStefan Hajnoczi2017-05-05 17:56:38 +0200
commitf03f9f0c10dcfadee5811d43240f0a6af230f1ce (patch)
tree15c0d55871e5f77c53dbd84cdeaf6c11629ef69a /chardev
parentMerge remote-tracking branch 'quintela/tags/migration/20170504' into staging (diff)
parents390x/3270: Mark non-migratable and enable the device (diff)
downloadqemu-f03f9f0c10dcfadee5811d43240f0a6af230f1ce.tar.gz
qemu-f03f9f0c10dcfadee5811d43240f0a6af230f1ce.tar.xz
qemu-f03f9f0c10dcfadee5811d43240f0a6af230f1ce.zip
Merge remote-tracking branch 'cohuck/tags/s390x-3270-20170504' into staging
Basic support for using channel-attached 3270 'green-screen' devices via tn3270. Actual handling of the data stream is delegated to x3270; more info at http://wiki.qemu.org/Features/3270 # gpg: Signature made Thu 04 May 2017 11:36:51 AM BST # gpg: using RSA key 0xDECF6B93C6F02FAF # gpg: Good signature from "Cornelia Huck <conny@cornelia-huck.de>" # gpg: aka "Cornelia Huck <cohuck@kernel.org>" # gpg: aka "Cornelia Huck <cornelia.huck@de.ibm.com>" # gpg: aka "Cornelia Huck <huckc@linux.vnet.ibm.com>" # Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0 18CE DECF 6B93 C6F0 2FAF * cohuck/tags/s390x-3270-20170504: s390x/3270: Mark non-migratable and enable the device s390x/3270: Detect for continued presence of a 3270 client s390x/3270: Add the TCP socket events handler for 3270 s390x/3270: 3270 data stream handling s390x/3270: Add emulated terminal3270 device s390x/3270: Add abstract emulated ccw-attached 3270 device s390x/css: Add an algorithm to find a free chpid chardev: Basic support for TN3270 Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'chardev')
-rw-r--r--chardev/char-socket.c76
-rw-r--r--chardev/char.c11
2 files changed, 65 insertions, 22 deletions
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 36ab0d633a..175fb8c3ec 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -55,6 +55,7 @@ typedef struct {
SocketAddress *addr;
bool is_listen;
bool is_telnet;
+ bool is_tn3270;
guint reconnect_timer;
int64_t reconnect_time;
@@ -141,19 +142,25 @@ static int tcp_chr_read_poll(void *opaque)
return s->max_size;
}
-#define IAC 255
-#define IAC_BREAK 243
static void tcp_chr_process_IAC_bytes(Chardev *chr,
SocketChardev *s,
uint8_t *buf, int *size)
{
- /* Handle any telnet client's basic IAC options to satisfy char by
- * char mode with no echo. All IAC options will be removed from
- * the buf and the do_telnetopt variable will be used to track the
- * state of the width of the IAC information.
+ /* Handle any telnet or tn3270 client's basic IAC options.
+ * For telnet options, it satisfies char by char mode with no echo.
+ * For tn3270 options, it satisfies binary mode with EOR.
+ * All IAC options will be removed from the buf and the do_opt
+ * pointer will be used to track the state of the width of the
+ * IAC information.
*
- * IAC commands come in sets of 3 bytes with the exception of the
- * "IAC BREAK" command and the double IAC.
+ * RFC854: "All TELNET commands consist of at least a two byte sequence.
+ * The commands dealing with option negotiation are three byte sequences,
+ * the third byte being the code for the option referenced."
+ * "IAC BREAK", "IAC IP", "IAC NOP" and the double IAC are two bytes.
+ * "IAC SB", "IAC SE" and "IAC EOR" are saved to split up data boundary
+ * for tn3270.
+ * NOP, Break and Interrupt Process(IP) might be encountered during a TN3270
+ * session, and NOP and IP need to be done later.
*/
int i;
@@ -174,6 +181,18 @@ static void tcp_chr_process_IAC_bytes(Chardev *chr,
/* Handle IAC break commands by sending a serial break */
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
s->do_telnetopt++;
+ } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_EOR
+ || (unsigned char)buf[i] == IAC_SB
+ || (unsigned char)buf[i] == IAC_SE)
+ && s->do_telnetopt == 2) {
+ buf[j++] = IAC;
+ buf[j++] = buf[i];
+ s->do_telnetopt++;
+ } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_IP
+ || (unsigned char)buf[i] == IAC_NOP)
+ && s->do_telnetopt == 2) {
+ /* TODO: IP and NOP need to be implemented later. */
+ s->do_telnetopt++;
}
s->do_telnetopt++;
}
@@ -512,7 +531,7 @@ static void tcp_chr_update_read_handler(Chardev *chr,
typedef struct {
Chardev *chr;
- char buf[12];
+ char buf[21];
size_t buflen;
} TCPChardevTelnetInit;
@@ -550,9 +569,6 @@ static void tcp_chr_telnet_init(Chardev *chr)
TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
size_t n = 0;
- init->chr = chr;
- init->buflen = 12;
-
#define IACSET(x, a, b, c) \
do { \
x[n++] = a; \
@@ -560,12 +576,26 @@ static void tcp_chr_telnet_init(Chardev *chr)
x[n++] = c; \
} while (0)
- /* Prep the telnet negotion to put telnet in binary,
- * no echo, single char mode */
- IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
- IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
- IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
- IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
+ init->chr = chr;
+ if (!s->is_tn3270) {
+ init->buflen = 12;
+ /* Prep the telnet negotion to put telnet in binary,
+ * no echo, single char mode */
+ IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
+ IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
+ IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
+ IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
+ } else {
+ init->buflen = 21;
+ /* Prep the TN3270 negotion based on RFC1576 */
+ IACSET(init->buf, 0xff, 0xfd, 0x19); /* IAC DO EOR */
+ IACSET(init->buf, 0xff, 0xfb, 0x19); /* IAC WILL EOR */
+ IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO BINARY */
+ IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL BINARY */
+ IACSET(init->buf, 0xff, 0xfd, 0x18); /* IAC DO TERMINAL TYPE */
+ IACSET(init->buf, 0xff, 0xfa, 0x18); /* IAC SB TERMINAL TYPE */
+ IACSET(init->buf, 0x01, 0xff, 0xf0); /* SEND IAC SE */
+ }
#undef IACSET
@@ -585,7 +615,8 @@ static void tcp_chr_tls_handshake(QIOTask *task,
if (qio_task_propagate_error(task, NULL)) {
tcp_chr_disconnect(chr);
} else {
- if (s->do_telnetopt) {
+ /* tn3270 does not support TLS yet */
+ if (s->do_telnetopt && !s->is_tn3270) {
tcp_chr_telnet_init(chr);
} else {
tcp_chr_connect(chr);
@@ -824,12 +855,14 @@ static void qmp_chardev_open_socket(Chardev *chr,
bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
bool is_listen = sock->has_server ? sock->server : true;
bool is_telnet = sock->has_telnet ? sock->telnet : false;
+ bool is_tn3270 = sock->has_tn3270 ? sock->tn3270 : false;
bool is_waitconnect = sock->has_wait ? sock->wait : false;
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
QIOChannelSocket *sioc = NULL;
s->is_listen = is_listen;
s->is_telnet = is_telnet;
+ s->is_tn3270 = is_tn3270;
s->do_nodelay = do_nodelay;
if (sock->tls_creds) {
Object *creds;
@@ -879,7 +912,7 @@ static void qmp_chardev_open_socket(Chardev *chr,
addr, is_listen, is_telnet);
if (is_listen) {
- if (is_telnet) {
+ if (is_telnet || is_tn3270) {
s->do_telnetopt = 1;
}
} else if (reconnect > 0) {
@@ -933,6 +966,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
bool is_listen = qemu_opt_get_bool(opts, "server", false);
bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
bool is_telnet = qemu_opt_get_bool(opts, "telnet", false);
+ bool is_tn3270 = qemu_opt_get_bool(opts, "tn3270", false);
bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true);
int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0);
const char *path = qemu_opt_get(opts, "path");
@@ -968,6 +1002,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
sock->server = is_listen;
sock->has_telnet = true;
sock->telnet = is_telnet;
+ sock->has_tn3270 = true;
+ sock->tn3270 = is_tn3270;
sock->has_wait = true;
sock->wait = is_waitconnect;
sock->has_reconnect = true;
diff --git a/chardev/char.c b/chardev/char.c
index 3df116350b..309734f2b7 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -696,7 +696,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
return opts;
}
if (strstart(filename, "tcp:", &p) ||
- strstart(filename, "telnet:", &p)) {
+ strstart(filename, "telnet:", &p) ||
+ strstart(filename, "tn3270:", &p)) {
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
@@ -712,8 +713,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
goto fail;
}
}
- if (strstart(filename, "telnet:", &p))
+ if (strstart(filename, "telnet:", &p)) {
qemu_opt_set(opts, "telnet", "on", &error_abort);
+ } else if (strstart(filename, "tn3270:", &p)) {
+ qemu_opt_set(opts, "tn3270", "on", &error_abort);
+ }
return opts;
}
if (strstart(filename, "udp:", &p)) {
@@ -1177,6 +1181,9 @@ QemuOptsList qemu_chardev_opts = {
.name = "telnet",
.type = QEMU_OPT_BOOL,
},{
+ .name = "tn3270",
+ .type = QEMU_OPT_BOOL,
+ },{
.name = "tls-creds",
.type = QEMU_OPT_STRING,
},{