Dario Faggioli
3206ea7c01
- Revert to revision 715. We're fixing bug 1199924, before moving to QEMU 7.0 OBS-URL: https://build.opensuse.org/request/show/979479 OBS-URL: https://build.opensuse.org/package/show/Virtualization/qemu?expand=0&rev=718
136 lines
4.8 KiB
Diff
136 lines
4.8 KiB
Diff
From: John Snow <jsnow@redhat.com>
|
|
Date: Mon, 31 Jan 2022 23:11:34 -0500
|
|
Subject: python/aqmp: add socket bind step to legacy.py
|
|
|
|
Git-commit: b0b662bb2b340d63529672b5bdae596a6243c4d0
|
|
|
|
The synchronous QMP library would bind to the server address during
|
|
__init__(). The new library delays this to the accept() call, because
|
|
binding occurs inside of the call to start_[unix_]server(), which is an
|
|
async method -- so it cannot happen during __init__ anymore.
|
|
|
|
Python 3.7+ adds the ability to create the server (and thus the bind()
|
|
call) and begin the active listening in separate steps, but we don't
|
|
have that functionality in 3.6, our current minimum.
|
|
|
|
Therefore ... Add a temporary workaround that allows the synchronous
|
|
version of the client to bind the socket in advance, guaranteeing that
|
|
there will be a UNIX socket in the filesystem ready for the QEMU client
|
|
to connect to without a race condition.
|
|
|
|
(Yes, it's a bit ugly. Fixing it more nicely will have to wait until our
|
|
minimum Python version is 3.7+.)
|
|
|
|
Signed-off-by: John Snow <jsnow@redhat.com>
|
|
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
|
|
Message-id: 20220201041134.1237016-5-jsnow@redhat.com
|
|
Signed-off-by: John Snow <jsnow@redhat.com>
|
|
Signed-off-by: Li Zhang <lizhang@suse.de>
|
|
---
|
|
python/qemu/aqmp/legacy.py | 3 +++
|
|
python/qemu/aqmp/protocol.py | 41 +++++++++++++++++++++++++++++++++---
|
|
2 files changed, 41 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py
|
|
index 0890f95b16875ecb815ed4560bc7..6baa5f3409a6b459c67097d3c2a0 100644
|
|
--- a/python/qemu/aqmp/legacy.py
|
|
+++ b/python/qemu/aqmp/legacy.py
|
|
@@ -56,6 +56,9 @@ class QEMUMonitorProtocol(qemu.qmp.QEMUMonitorProtocol):
|
|
self._address = address
|
|
self._timeout: Optional[float] = None
|
|
|
|
+ if server:
|
|
+ self._aqmp._bind_hack(address) # pylint: disable=protected-access
|
|
+
|
|
_T = TypeVar('_T')
|
|
|
|
def _sync(
|
|
diff --git a/python/qemu/aqmp/protocol.py b/python/qemu/aqmp/protocol.py
|
|
index 50e973c2f2dc9c5fa759380ab3e9..33358f5cd72b61bd060b8dea6091 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.")
|
|
|