int in_len;
int out_len;
bool handshake_done;
+ guint timer_tag;
} Terminal3270;
#define TYPE_TERMINAL_3270 "x-terminal3270"
css_conditional_io_interrupt(sch);
}
+/*
+ * Called when the interval is timeout to detect
+ * if the client is still alive by Timing Mark.
+ */
+static gboolean send_timing_mark_cb(gpointer opaque)
+{
+ Terminal3270 *t = opaque;
+ const uint8_t timing[] = {0xff, 0xfd, 0x06};
+
+ qemu_chr_fe_write_all(&t->chr, timing, sizeof(timing));
+ return true;
+}
+
/*
* Receive inbound data from socket.
* For data given to guest, drop the data boundary IAC, IAC_EOR.
assert(size <= (INPUT_BUFFER_SIZE - t->in_len));
+ if (t->timer_tag) {
+ g_source_remove(t->timer_tag);
+ t->timer_tag = 0;
+ }
+ t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t);
+
memcpy(&t->inv[t->in_len], buf, size);
t->in_len += size;
if (t->in_len < 2) {
t->in_len = 0;
t->out_len = 0;
t->handshake_done = false;
+ if (t->timer_tag) {
+ g_source_remove(t->timer_tag);
+ t->timer_tag = 0;
+ }
switch (event) {
case CHR_EVENT_OPENED:
* char-socket.c. Once qemu receives the terminal-type of the
* client, mark handshake done and trigger everything rolling again.
*/
+ t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t);
break;
case CHR_EVENT_CLOSED:
sch->curr_status.scsw.dstat = SCSW_DSTAT_DEVICE_END;
assert(count <= (OUTPUT_BUFFER_SIZE - 3) / 2);
if (!t->handshake_done) {
- /*
- * Before having finished 3270 negotiation,
- * sending outbound data is prohibited.
- */
- return 0;
+ if (!(t->outv[0] == IAC && t->outv[1] != IAC)) {
+ /*
+ * Before having finished 3270 negotiation,
+ * sending outbound data except protocol options is prohibited.
+ */
+ return 0;
+ }
}
if (!qemu_chr_fe_get_driver(&t->chr)) {
/* We just say we consumed all data if there's no backend. */