Skip to content

Commit

Permalink
python/machine: use socketpair() for console connections
Browse files Browse the repository at this point in the history
Create a socketpair for the console output. This should help eliminate
race conditions around console text early in the boot process that might
otherwise have been dropped on the floor before being able to connect to
QEMU under "server,nowait".

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-id: 20230928044943.849073-5-jsnow@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
  • Loading branch information
jnsnow committed Oct 11, 2023
1 parent 5f263cb commit 1d4796c
Showing 1 changed file with 27 additions and 3 deletions.
30 changes: 27 additions & 3 deletions python/qemu/machine/machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ def __init__(self,

self._name = name or f"{id(self):x}"
self._sock_pair: Optional[Tuple[socket.socket, socket.socket]] = None
self._cons_sock_pair: Optional[
Tuple[socket.socket, socket.socket]] = None
self._temp_dir: Optional[str] = None
self._base_temp_dir = base_temp_dir
self._sock_dir = sock_dir
Expand Down Expand Up @@ -316,8 +318,9 @@ def _base_args(self) -> List[str]:
for _ in range(self._console_index):
args.extend(['-serial', 'null'])
if self._console_set:
chardev = ('socket,id=console,path=%s,server=on,wait=off' %
self._console_address)
assert self._cons_sock_pair is not None
fd = self._cons_sock_pair[0].fileno()
chardev = f"socket,id=console,fd={fd}"
args.extend(['-chardev', chardev])
if self._console_device_type is None:
args.extend(['-serial', 'chardev:console'])
Expand Down Expand Up @@ -352,6 +355,10 @@ def _pre_launch(self) -> None:
nickname=self._name
)

if self._console_set:
self._cons_sock_pair = socket.socketpair()
os.set_inheritable(self._cons_sock_pair[0].fileno(), True)

# NOTE: Make sure any opened resources are *definitely* freed in
# _post_shutdown()!
# pylint: disable=consider-using-with
Expand All @@ -369,6 +376,9 @@ def _pre_launch(self) -> None:
def _post_launch(self) -> None:
if self._sock_pair:
self._sock_pair[0].close()
if self._cons_sock_pair:
self._cons_sock_pair[0].close()

if self._qmp_connection:
if self._sock_pair:
self._qmp.connect()
Expand Down Expand Up @@ -524,6 +534,11 @@ def _early_cleanup(self) -> None:
self._console_socket.close()
self._console_socket = None

if self._cons_sock_pair:
self._cons_sock_pair[0].close()
self._cons_sock_pair[1].close()
self._cons_sock_pair = None

def _hard_shutdown(self) -> None:
"""
Perform early cleanup, kill the VM, and wait for it to terminate.
Expand Down Expand Up @@ -885,10 +900,19 @@ def console_socket(self) -> socket.socket:
"""
if self._console_socket is None:
LOG.debug("Opening console socket")
if not self._console_set:
raise QEMUMachineError(
"Attempt to access console socket with no connection")
assert self._cons_sock_pair is not None
# os.dup() is used here for sock_fd because otherwise we'd
# have two rich python socket objects that would each try to
# close the same underlying fd when either one gets garbage
# collected.
self._console_socket = console_socket.ConsoleSocket(
self._console_address,
sock_fd=os.dup(self._cons_sock_pair[1].fileno()),
file=self._console_log_path,
drain=self._drain_console)
self._cons_sock_pair[1].close()
return self._console_socket

@property
Expand Down

0 comments on commit 1d4796c

Please sign in to comment.