2 QEMU Console Socket Module:
4 This python module implements a ConsoleSocket object,
5 which can drain a socket and optionally dump the bytes to file.
7 # Copyright 2020 Linaro
10 # Robert Foley <robert.foley@linaro.org>
12 # This code is licensed under the GPL version 2 or later. See
13 # the COPYING file in the top-level directory.
18 from collections
import deque
22 class ConsoleSocket(socket
.socket
):
24 ConsoleSocket represents a socket attached to a char device.
26 Optionally (if drain==True), drains the socket and places the bytes
27 into an in memory buffer for later processing.
29 Optionally a file path can be passed in and we will also
30 dump the characters to this file for debugging purposes.
32 def __init__(self
, address
, file=None, drain
=False):
33 self
._recv
_timeout
_sec
= 300
34 self
._sleep
_time
= 0.5
35 self
._buffer
= deque()
36 socket
.socket
.__init
__(self
, socket
.AF_UNIX
, socket
.SOCK_STREAM
)
40 self
._logfile
= open(file, "w")
43 self
._drain
_thread
= self
._thread
_start
()
45 self
._drain
_thread
= None
48 """Drains the socket and runs while the socket is open."""
52 except socket
.timeout
:
53 # The socket is expected to timeout since we set a
54 # short timeout to allow the thread to exit when
55 # self._open is set to False.
56 time
.sleep(self
._sleep
_time
)
58 def _thread_start(self
):
59 """Kick off a thread to drain the socket."""
60 # Configure socket to not block and timeout.
61 # This allows our drain thread to not block
62 # on recieve and exit smoothly.
63 socket
.socket
.setblocking(self
, False)
64 socket
.socket
.settimeout(self
, 1)
65 drain_thread
= threading
.Thread(target
=self
._drain
_fn
)
66 drain_thread
.daemon
= True
71 """Close the base object and wait for the thread to terminate"""
74 if self
._drain
_thread
is not None:
75 thread
, self
._drain
_thread
= self
._drain
_thread
, None
77 socket
.socket
.close(self
)
82 def _drain_socket(self
):
83 """process arriving characters into in memory _buffer"""
84 data
= socket
.socket
.recv(self
, 1)
85 # latin1 is needed since there are some chars
86 # we are receiving that cannot be encoded to utf-8
87 # such as 0xe2, 0x80, 0xA6.
88 string
= data
.decode("latin1")
90 self
._logfile
.write("{}".format(string
))
93 self
._buffer
.extend(c
)
95 def recv(self
, bufsize
=1):
96 """Return chars from in memory buffer.
97 Maintains the same API as socket.socket.recv.
99 if self
._drain
_thread
is None:
100 # Not buffering the socket, pass thru to socket.
101 return socket
.socket
.recv(self
, bufsize
)
102 start_time
= time
.time()
103 while len(self
._buffer
) < bufsize
:
104 time
.sleep(self
._sleep
_time
)
105 elapsed_sec
= time
.time() - start_time
106 if elapsed_sec
> self
._recv
_timeout
_sec
:
108 chars
= ''.join([self
._buffer
.popleft() for i
in range(bufsize
)])
109 # We choose to use latin1 to remain consistent with
110 # handle_read() and give back the same data as the user would
111 # receive if they were reading directly from the
112 # socket w/o our intervention.
113 return chars
.encode("latin1")
115 def setblocking(self
, value
):
116 """When not draining we pass thru to the socket,
117 since when draining we control socket blocking.
119 if self
._drain
_thread
is None:
120 socket
.socket
.setblocking(self
, value
)
122 def settimeout(self
, seconds
):
123 """When not draining we pass thru to the socket,
124 since when draining we control the timeout.
126 if seconds
is not None:
127 self
._recv
_timeout
_sec
= seconds
128 if self
._drain
_thread
is None:
129 socket
.socket
.settimeout(self
, seconds
)