HACK: Workaround weird paramiko socket behaviour
When the far side disappears, such as on reboot, mdt shell can get
stuck 20 minutes. If we enable keepalive messages and add a timeout
to select() we can detect that tx of keepalives aren't progressing
and emulate a timeout after a more reasonable 20 seconds.
Change-Id: Icdaf51825fcf7c779afa6776b0f4f78f087f8864
diff --git a/mdt/console.py b/mdt/console.py
index 3851e37..5e31fcb 100644
--- a/mdt/console.py
+++ b/mdt/console.py
@@ -28,6 +28,7 @@
TYPE_TERMINAL_OUTPUT = 1
TYPE_REMOTE_CLOSED = 2
TYPE_SOCKET_TIMEOUT = 3
+KEEP_ALIVE_SECONDS = 10
class ConnectionClosedError(Exception):
@@ -52,12 +53,24 @@
self.channel = channel
self.inputfile = inputfile
self.has_tty = False
+ self.linux = os.uname()[0] == 'Linux'
def _updateWindowSize(self, signum, stackFrame):
if self.has_tty:
(rows, columns) = GetTtyWindowSize(self.inputfile)
self.channel.resize_pty(columns, rows, 0, 0)
+ def _socketSendQueueLevel(self):
+ # Only supported on Linux
+ if not self.linux:
+ return 0
+
+ import fcntl
+ import struct
+ SIOCOUTQ = 0x5411
+ sock = self.channel.get_transport().sock
+ return struct.unpack("I", fcntl.ioctl(sock.fileno(), SIOCOUTQ, '\0\0\0\0'))[0]
+
def run(self):
import termios
import tty
@@ -94,11 +107,25 @@
try:
self.channel.settimeout(0)
+ self.channel.get_transport().set_keepalive(KEEP_ALIVE_SECONDS)
+ timeouts = 0
+ tx_level = 0
while True:
read, write, exception = select.select([self.channel,
self.inputfile],
- [], [])
+ [], [], KEEP_ALIVE_SECONDS + 1)
+ timeout = not read and not write and not exception
+ if self.linux and timeout:
+ timeouts += 1
+ if timeouts == 1:
+ tx_level = self._socketSendQueueLevel()
+ else:
+ current_tx_level = self._socketSendQueueLevel()
+ if current_tx_level and current_tx_level >= tx_level:
+ raise SocketTimeoutError(socket.timeout())
+ else:
+ timeouts = 0
# data from device to host
if self.channel in read: