summary refs log tree commit diff stats
path: root/python/qemu/aqmp/protocol.py
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2022-02-03 15:42:28 +0000
committerPeter Maydell <peter.maydell@linaro.org>2022-02-03 15:42:28 +0000
commit31f59af395922b7f40799e75db6e15ff52d8f94a (patch)
tree3dad1f53308b49c39d898334ff59d3cea58d3855 /python/qemu/aqmp/protocol.py
parent8f3e5ce773c62bb5c4a847f3a9a5c98bbb3b359f (diff)
parentb0b662bb2b340d63529672b5bdae596a6243c4d0 (diff)
downloadfocaccia-qemu-31f59af395922b7f40799e75db6e15ff52d8f94a.tar.gz
focaccia-qemu-31f59af395922b7f40799e75db6e15ff52d8f94a.zip
Merge remote-tracking branch 'remotes/jsnow-gitlab/tags/python-pull-request' into staging
Python patches

Peter: I expect this to address the iotest 040,041 failures you observed
on NetBSD. If it doesn't, let me know.

# gpg: Signature made Thu 03 Feb 2022 01:59:32 GMT
# gpg:                using RSA key F9B7ABDBBCACDF95BE76CBD07DEF8106AAFC390E
# gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" [full]
# Primary key fingerprint: FAEB 9711 A12C F475 812F  18F2 88A9 064D 1835 61EB
#      Subkey fingerprint: F9B7 ABDB BCAC DF95 BE76  CBD0 7DEF 8106 AAFC 390E

* remotes/jsnow-gitlab/tags/python-pull-request:
  python/aqmp: add socket bind step to legacy.py
  python: upgrade mypy to 0.780
  python/machine: raise VMLaunchFailure exception from launch()
  python/aqmp: Fix negotiation with pre-"oob" QEMU

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'python/qemu/aqmp/protocol.py')
-rw-r--r--python/qemu/aqmp/protocol.py41
1 files changed, 38 insertions, 3 deletions
diff --git a/python/qemu/aqmp/protocol.py b/python/qemu/aqmp/protocol.py
index 50e973c2f2..33358f5cd7 100644
--- a/python/qemu/aqmp/protocol.py
+++ b/python/qemu/aqmp/protocol.py
@@ -15,6 +15,7 @@ from asyncio import StreamReader, StreamWriter
 from enum import Enum
 from functools import wraps
 import logging
+import socket
 from ssl import SSLContext
 from typing import (
     Any,
@@ -238,6 +239,9 @@ class AsyncProtocol(Generic[T]):
         self._runstate = Runstate.IDLE
         self._runstate_changed: Optional[asyncio.Event] = None
 
+        # Workaround for bind()
+        self._sock: Optional[socket.socket] = None
+
     def __repr__(self) -> str:
         cls_name = type(self).__name__
         tokens = []
@@ -427,6 +431,34 @@ class AsyncProtocol(Generic[T]):
         else:
             await self._do_connect(address, ssl)
 
+    def _bind_hack(self, address: Union[str, Tuple[str, int]]) -> None:
+        """
+        Used to create a socket in advance of accept().
+
+        This is a workaround to ensure that we can guarantee timing of
+        precisely when a socket exists to avoid a connection attempt
+        bouncing off of nothing.
+
+        Python 3.7+ adds a feature to separate the server creation and
+        listening phases instead, and should be used instead of this
+        hack.
+        """
+        if isinstance(address, tuple):
+            family = socket.AF_INET
+        else:
+            family = socket.AF_UNIX
+
+        sock = socket.socket(family, socket.SOCK_STREAM)
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
+        try:
+            sock.bind(address)
+        except:
+            sock.close()
+            raise
+
+        self._sock = sock
+
     @upper_half
     async def _do_accept(self, address: SocketAddrT,
                          ssl: Optional[SSLContext] = None) -> None:
@@ -464,24 +496,27 @@ class AsyncProtocol(Generic[T]):
         if isinstance(address, tuple):
             coro = asyncio.start_server(
                 _client_connected_cb,
-                host=address[0],
-                port=address[1],
+                host=None if self._sock else address[0],
+                port=None if self._sock else address[1],
                 ssl=ssl,
                 backlog=1,
                 limit=self._limit,
+                sock=self._sock,
             )
         else:
             coro = asyncio.start_unix_server(
                 _client_connected_cb,
-                path=address,
+                path=None if self._sock else address,
                 ssl=ssl,
                 backlog=1,
                 limit=self._limit,
+                sock=self._sock,
             )
 
         server = await coro     # Starts listening
         await connected.wait()  # Waits for the callback to fire (and finish)
         assert server is None
+        self._sock = None
 
         self.logger.debug("Connection accepted.")