From 6e2f6ec5615f50c38e21542bb66bb690a289cbfc Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:25 -0400 Subject: python/aqmp: add send_fd_scm Add an implementation for send_fd_scm to the async QMP implementation. Like socket_scm_helper mentions, a non-empty payload is required for QEMU to process the ancillary data. A space is most useful because it does not disturb the parsing of subsequent JSON objects. A note on "voiding the warranty": Python 3.11 removes support for calling sendmsg directly from a transport's socket. There is no other interface for doing this, our use case is, I suspect, "quite unique". As far as I can tell, this is safe to do -- send_fd_scm is a synchronous function and we can be guaranteed that the async coroutines will *not* be running when it is invoked. In testing, it works correctly. I investigated quite thoroughly the possibility of creating my own asyncio Transport (The class that ultimately manages the raw socket object) so that I could manage the socket myself, but this is so wildly invasive and unportable I scrapped the idea. It would involve a lot of copy-pasting of various python utilities and classes just to re-create the same infrastructure, and for extremely little benefit. Nah. Just boldly void the warranty instead, while I try to follow up on https://bugs.python.org/issue43232 Signed-off-by: John Snow Reviewed-by: Paolo Bonzini Reviewed-by: Eric Blake Message-id: 20210923004938.3999963-5-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/qmp_client.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'python/qemu/aqmp/qmp_client.py') diff --git a/python/qemu/aqmp/qmp_client.py b/python/qemu/aqmp/qmp_client.py index d2ad7459f9..f987da02eb 100644 --- a/python/qemu/aqmp/qmp_client.py +++ b/python/qemu/aqmp/qmp_client.py @@ -9,6 +9,8 @@ accept an incoming connection from that server. import asyncio import logging +import socket +import struct from typing import ( Dict, List, @@ -624,3 +626,23 @@ class QMPClient(AsyncProtocol[Message], Events): """ msg = self.make_execute_msg(cmd, arguments, oob=oob) return await self.execute_msg(msg) + + @upper_half + @require(Runstate.RUNNING) + def send_fd_scm(self, fd: int) -> None: + """ + Send a file descriptor to the remote via SCM_RIGHTS. + """ + assert self._writer is not None + sock = self._writer.transport.get_extra_info('socket') + + if sock.family != socket.AF_UNIX: + raise AQMPError("Sending file descriptors requires a UNIX socket.") + + # Void the warranty sticker. + # Access to sendmsg in asyncio is scheduled for removal in Python 3.11. + sock = sock._sock # pylint: disable=protected-access + sock.sendmsg( + [b' '], + [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))] + ) -- cgit v1.2.3-55-g7522