From 0257209a09793bfaf1b662faad5558e7011e748a Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:22 -0400 Subject: [PATCH 01/10] python/aqmp: add greeting property to QMPClient Expose the greeting as a read-only property of QMPClient so it can be retrieved at-will. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-2-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/qmp_client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/qemu/aqmp/qmp_client.py b/python/qemu/aqmp/qmp_client.py index 82e9dab124..d2ad7459f9 100644 --- a/python/qemu/aqmp/qmp_client.py +++ b/python/qemu/aqmp/qmp_client.py @@ -224,6 +224,11 @@ class QMPClient(AsyncProtocol[Message], Events): 'asyncio.Queue[QMPClient._PendingT]' ] = {} + @property + def greeting(self) -> Optional[Greeting]: + """The `Greeting` from the QMP server, if any.""" + return self._greeting + @upper_half async def _establish_session(self) -> None: """ From 16cce725ed87e3ed5901c93e61514840c989216a Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:23 -0400 Subject: [PATCH 02/10] python/aqmp: add .empty() method to EventListener Synchronous clients may want to know if they're about to block waiting for an event or not. A method such as this is necessary to implement a compatible interface for the old QEMUMonitorProtocol using the new async internals. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-3-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/events.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/qemu/aqmp/events.py b/python/qemu/aqmp/events.py index fb81d21610..271899f6b8 100644 --- a/python/qemu/aqmp/events.py +++ b/python/qemu/aqmp/events.py @@ -556,6 +556,12 @@ class EventListener: """ return await self._queue.get() + def empty(self) -> bool: + """ + Return `True` if there are no pending events. + """ + return self._queue.empty() + def clear(self) -> None: """ Clear this listener of all pending events. From 6bfebc7306e42910cd33553d9ed385ef475d8196 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:24 -0400 Subject: [PATCH 03/10] python/aqmp: Return cleared events from EventListener.clear() This serves two purposes: (1) It is now possible to discern whether or not clear() removed any event(s) from the queue with absolute certainty, and (2) It is now very easy to get a List of all pending events in one chunk, which is useful for the sync bridge. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-4-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/events.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/python/qemu/aqmp/events.py b/python/qemu/aqmp/events.py index 271899f6b8..5f7150c78d 100644 --- a/python/qemu/aqmp/events.py +++ b/python/qemu/aqmp/events.py @@ -562,7 +562,7 @@ class EventListener: """ return self._queue.empty() - def clear(self) -> None: + def clear(self) -> List[Message]: """ Clear this listener of all pending events. @@ -570,17 +570,22 @@ class EventListener: pending FIFO queue synchronously. It can be also be used to manually clear any pending events, if desired. + :return: The cleared events, if any. + .. warning:: Take care when discarding events. Cleared events will be silently tossed on the floor. All events that were ever accepted by this listener are visible in `history()`. """ + events = [] while True: try: - self._queue.get_nowait() + events.append(self._queue.get_nowait()) except asyncio.QueueEmpty: break + return events + def __aiter__(self) -> AsyncIterator[Message]: return self From 6e2f6ec5615f50c38e21542bb66bb690a289cbfc Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:25 -0400 Subject: [PATCH 04/10] 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(+) 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))] + ) From 58026b11f36f9a19fd35fdfbfa50be7bd78e70a5 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:26 -0400 Subject: [PATCH 05/10] python/aqmp: Add dict conversion method to Greeting object The iotests interface expects to return the greeting as a dict; AQMP offers it as a rich object. Signed-off-by: John Snow Reviewed-by: Paolo Bonzini Reviewed-by: Eric Blake Message-id: 20210923004938.3999963-6-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/models.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/python/qemu/aqmp/models.py b/python/qemu/aqmp/models.py index 24c94123ac..de87f87804 100644 --- a/python/qemu/aqmp/models.py +++ b/python/qemu/aqmp/models.py @@ -8,8 +8,10 @@ data to make sure it conforms to spec. # pylint: disable=too-few-public-methods from collections import abc +import copy from typing import ( Any, + Dict, Mapping, Optional, Sequence, @@ -66,6 +68,17 @@ class Greeting(Model): self._check_member('QMP', abc.Mapping, "JSON object") self.QMP = QMPGreeting(self._raw['QMP']) + def _asdict(self) -> Dict[str, object]: + """ + For compatibility with the iotests sync QMP wrapper. + + The legacy QMP interface needs Greetings as a garden-variety Dict. + + This interface is private in the hopes that it will be able to + be dropped again in the near-future. Caller beware! + """ + return dict(copy.deepcopy(self._raw)) + class QMPGreeting(Model): """ From 3e55dc35b8ed8cea53ef2604874d2fe882fbadc1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:27 -0400 Subject: [PATCH 06/10] python/aqmp: Reduce severity of EOFError-caused loop terminations When we encounter an EOFError, we don't know if it's an "error" in the perspective of the user of the library yet. Therefore, we should not log it as an error. Reduce the severity of this logging message to "INFO" to indicate that it's something that we expect to occur during the normal operation of the library. Signed-off-by: John Snow Reviewed-by: Paolo Bonzini Reviewed-by: Eric Blake Message-id: 20210923004938.3999963-7-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/protocol.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/qemu/aqmp/protocol.py b/python/qemu/aqmp/protocol.py index 32e78749c1..ae1df24026 100644 --- a/python/qemu/aqmp/protocol.py +++ b/python/qemu/aqmp/protocol.py @@ -721,8 +721,11 @@ class AsyncProtocol(Generic[T]): self.logger.debug("Task.%s: cancelled.", name) return except BaseException as err: - self.logger.error("Task.%s: %s", - name, exception_summary(err)) + self.logger.log( + logging.INFO if isinstance(err, EOFError) else logging.ERROR, + "Task.%s: %s", + name, exception_summary(err) + ) self.logger.debug("Task.%s: failure:\n%s\n", name, pretty_traceback()) self._schedule_disconnect() From 3a3d84f5ec0bca2586a1a83691182576a9447130 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:28 -0400 Subject: [PATCH 07/10] python/aqmp: Disable logging messages by default AQMP is a library, and ideally it should not print error diagnostics unless a user opts into seeing them. By default, Python will print all WARNING, ERROR or CRITICAL messages to screen if no logging configuration has been created by a client application. In AQMP's case, ERROR logging statements are used to report additional detail about runtime failures that will also eventually be reported to the client library via an Exception, so these messages should not be rendered by default. (Why bother to have them at all, then? In async contexts, there may be multiple Exceptions and we are only able to report one of them back to the client application. It is not reasonably easy to predict ahead of time if one or more of these Exceptions will be squelched. Therefore, it's useful to log intermediate failures to help make sense of the ultimate, resulting failure.) Add a NullHandler that will suppress these messages until a client application opts into logging via logging.basicConfig or similar. Note that upon calling basicConfig(), this handler will *not* suppress these messages from being displayed by the client's configuration. Signed-off-by: John Snow Reviewed-by: Paolo Bonzini Reviewed-by: Eric Blake Message-id: 20210923004938.3999963-8-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/qemu/aqmp/__init__.py b/python/qemu/aqmp/__init__.py index ab1782999c..d1b0e4dc3d 100644 --- a/python/qemu/aqmp/__init__.py +++ b/python/qemu/aqmp/__init__.py @@ -21,6 +21,7 @@ managing QMP events. # This work is licensed under the terms of the GNU GPL, version 2. See # the COPYING file in the top-level directory. +import logging import warnings from .error import AQMPError @@ -41,6 +42,9 @@ Proceed with caution! warnings.warn(_WMSG, FutureWarning) +# Suppress logging unless an application engages it. +logging.getLogger('qemu.aqmp').addHandler(logging.NullHandler()) + # The order of these fields impact the Sphinx documentation order. __all__ = ( From d911accf0a88070120f7cc71c065fb797484d9b7 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:29 -0400 Subject: [PATCH 08/10] python/qmp: clear events on get_events() call All callers in the tree *already* clear the events after a call to get_events(). Do it automatically instead and update callsites to remove the manual clear call. These semantics are quite a bit easier to emulate with async QMP, and nobody appears to be abusing some emergent properties of what happens if you decide not to clear them, so let's dial down to the dumber, simpler thing. Specifically: callers of clear() right after a call to get_events() are more likely expressing their desire to not see any events they just retrieved, whereas callers of clear_events() not in relation to a recent call to pull_event/get_events are likely expressing their desire to simply drop *all* pending events straight onto the floor. In the sync world, this is safe enough; in the async world it's nearly impossible to promise that nothing happens between getting and clearing the events. Making the retrieval also clear the queue is vastly simpler. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-9-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 1 - python/qemu/qmp/__init__.py | 6 ++++-- python/qemu/qmp/qmp_shell.py | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 34131884a5..ae945ca3c9 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -631,7 +631,6 @@ class QEMUMachine: events = self._qmp.get_events(wait=wait) events.extend(self._events) del self._events[:] - self._qmp.clear_events() return events @staticmethod diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py index 269516a79b..c27594b66a 100644 --- a/python/qemu/qmp/__init__.py +++ b/python/qemu/qmp/__init__.py @@ -361,7 +361,7 @@ class QEMUMonitorProtocol: def get_events(self, wait: bool = False) -> List[QMPMessage]: """ - Get a list of available QMP events. + Get a list of available QMP events and clear all pending events. @param wait (bool): block until an event is available. @param wait (float): If wait is a float, treat it as a timeout value. @@ -374,7 +374,9 @@ class QEMUMonitorProtocol: @return The list of available QMP events. """ self.__get_events(wait) - return self.__events + events = self.__events + self.__events = [] + return events def clear_events(self) -> None: """ diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index 337acfce2d..e7d7eb18f1 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -381,7 +381,6 @@ class QMPShell(qmp.QEMUMonitorProtocol): if cmdline == '': for event in self.get_events(): print(event) - self.clear_events() return True return self._execute_cmd(cmdline) From 514d00df5f44f220d0b97cc71323275067d3e60e Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:30 -0400 Subject: [PATCH 09/10] python/qmp: add send_fd_scm directly to QEMUMonitorProtocol It turns out you can do this directly from Python ... and because of this, you don't need to worry about setting the inheritability of the fds or spawning another process. Doing this is helpful because it allows QEMUMonitorProtocol to keep its file descriptor and socket object as private implementation details. /that/ is helpful in turn because it allows me to write a compatible, alternative implementation. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-10-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 44 +++++++--------------------------- python/qemu/qmp/__init__.py | 23 ++++++++---------- 2 files changed, 19 insertions(+), 48 deletions(-) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index ae945ca3c9..1c6532a3d6 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -213,48 +213,22 @@ class QEMUMachine: def send_fd_scm(self, fd: Optional[int] = None, file_path: Optional[str] = None) -> int: """ - Send an fd or file_path to socket_scm_helper. + Send an fd or file_path to the remote via SCM_RIGHTS. - Exactly one of fd and file_path must be given. - If it is file_path, the helper will open that file and pass its own fd. + Exactly one of fd and file_path must be given. If it is + file_path, the file will be opened read-only and the new file + descriptor will be sent to the remote. """ - # In iotest.py, the qmp should always use unix socket. - assert self._qmp.is_scm_available() - if self._socket_scm_helper is None: - raise QEMUMachineError("No path to socket_scm_helper set") - if not os.path.exists(self._socket_scm_helper): - raise QEMUMachineError("%s does not exist" % - self._socket_scm_helper) - - # This did not exist before 3.4, but since then it is - # mandatory for our purpose - if hasattr(os, 'set_inheritable'): - os.set_inheritable(self._qmp.get_sock_fd(), True) - if fd is not None: - os.set_inheritable(fd, True) - - fd_param = ["%s" % self._socket_scm_helper, - "%d" % self._qmp.get_sock_fd()] - if file_path is not None: assert fd is None - fd_param.append(file_path) + with open(file_path, "rb") as passfile: + fd = passfile.fileno() + self._qmp.send_fd_scm(fd) else: assert fd is not None - fd_param.append(str(fd)) + self._qmp.send_fd_scm(fd) - proc = subprocess.run( - fd_param, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - check=False, - close_fds=False, - ) - if proc.stdout: - LOG.debug(proc.stdout) - - return proc.returncode + return 0 @staticmethod def _remove_if_exists(path: str) -> None: diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py index c27594b66a..358c0971d0 100644 --- a/python/qemu/qmp/__init__.py +++ b/python/qemu/qmp/__init__.py @@ -21,6 +21,7 @@ import errno import json import logging import socket +import struct from types import TracebackType from typing import ( Any, @@ -408,18 +409,14 @@ class QEMUMonitorProtocol: raise ValueError(msg) self.__sock.settimeout(timeout) - def get_sock_fd(self) -> int: + def send_fd_scm(self, fd: int) -> None: """ - Get the socket file descriptor. + Send a file descriptor to the remote via SCM_RIGHTS. + """ + if self.__sock.family != socket.AF_UNIX: + raise RuntimeError("Can't use SCM_RIGHTS on non-AF_UNIX socket.") - @return The file descriptor number. - """ - return self.__sock.fileno() - - def is_scm_available(self) -> bool: - """ - Check if the socket allows for SCM_RIGHTS. - - @return True if SCM_RIGHTS is available, otherwise False. - """ - return self.__sock.family == socket.AF_UNIX + self.__sock.sendmsg( + [b' '], + [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))] + ) From c163c723ef92d0f629d015902396f2c67328b2e5 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 22 Sep 2021 20:49:31 -0400 Subject: [PATCH 10/10] python, iotests: remove socket_scm_helper It's not used anymore, now. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Paolo Bonzini Message-id: 20210923004938.3999963-11-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 3 - python/qemu/machine/qtest.py | 2 - tests/Makefile.include | 1 - tests/meson.build | 4 - tests/qemu-iotests/iotests.py | 3 - tests/qemu-iotests/meson.build | 5 - tests/qemu-iotests/socket_scm_helper.c | 136 ------------------------- tests/qemu-iotests/testenv.py | 8 +- 8 files changed, 1 insertion(+), 161 deletions(-) delete mode 100644 tests/qemu-iotests/meson.build delete mode 100644 tests/qemu-iotests/socket_scm_helper.c diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 1c6532a3d6..056d340e35 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -98,7 +98,6 @@ class QEMUMachine: name: Optional[str] = None, base_temp_dir: str = "/var/tmp", monitor_address: Optional[SocketAddrT] = None, - socket_scm_helper: Optional[str] = None, sock_dir: Optional[str] = None, drain_console: bool = False, console_log: Optional[str] = None, @@ -113,7 +112,6 @@ class QEMUMachine: @param name: prefix for socket and log file names (default: qemu-PID) @param base_temp_dir: default location where temp files are created @param monitor_address: address for QMP monitor - @param socket_scm_helper: helper program, required for send_fd_scm() @param sock_dir: where to create socket (defaults to base_temp_dir) @param drain_console: (optional) True to drain console socket to buffer @param console_log: (optional) path to console log file @@ -134,7 +132,6 @@ class QEMUMachine: self._base_temp_dir = base_temp_dir self._sock_dir = sock_dir or self._base_temp_dir self._log_dir = log_dir - self._socket_scm_helper = socket_scm_helper if monitor_address is not None: self._monitor_address = monitor_address diff --git a/python/qemu/machine/qtest.py b/python/qemu/machine/qtest.py index 395cc8fbfe..f2f9aaa5e5 100644 --- a/python/qemu/machine/qtest.py +++ b/python/qemu/machine/qtest.py @@ -115,7 +115,6 @@ class QEMUQtestMachine(QEMUMachine): wrapper: Sequence[str] = (), name: Optional[str] = None, base_temp_dir: str = "/var/tmp", - socket_scm_helper: Optional[str] = None, sock_dir: Optional[str] = None, qmp_timer: Optional[float] = None): # pylint: disable=too-many-arguments @@ -126,7 +125,6 @@ class QEMUQtestMachine(QEMUMachine): sock_dir = base_temp_dir super().__init__(binary, args, wrapper=wrapper, name=name, base_temp_dir=base_temp_dir, - socket_scm_helper=socket_scm_helper, sock_dir=sock_dir, qmp_timer=qmp_timer) self._qtest: Optional[QEMUQtestProtocol] = None self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") diff --git a/tests/Makefile.include b/tests/Makefile.include index 7426522bbe..7bb8961515 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -148,7 +148,6 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images check: ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy) -QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXESUF) check: check-block export PYTHON check-block: $(SRC_PATH)/tests/check-block.sh qemu-img$(EXESUF) \ diff --git a/tests/meson.build b/tests/meson.build index 55a7b08275..3f3882748a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -67,10 +67,6 @@ if have_tools and 'CONFIG_VHOST_USER' in config_host and 'CONFIG_LINUX' in confi dependencies: [qemuutil, vhost_user]) endif -if have_system and 'CONFIG_POSIX' in config_host - subdir('qemu-iotests') -endif - test('decodetree', sh, args: [ files('decode/check.sh'), config_host['PYTHON'], files('../scripts/decodetree.py') ], workdir: meson.current_source_dir() / 'decode', diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index b06ad76e0c..e5fff6ddcf 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -107,8 +107,6 @@ if os.environ.get('VALGRIND_QEMU') == "y" and \ qemu_valgrind = ['valgrind', valgrind_logfile, '--error-exitcode=99'] -socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper') - luks_default_secret_object = 'secret,id=keysec0,data=' + \ os.environ.get('IMGKEYSECRET', '') luks_default_key_secret_opt = 'key-secret=keysec0' @@ -598,7 +596,6 @@ class VM(qtest.QEMUQtestMachine): super().__init__(qemu_prog, qemu_opts, wrapper=wrapper, name=name, base_temp_dir=test_dir, - socket_scm_helper=socket_scm_helper, sock_dir=sock_dir, qmp_timer=timer) self._num_drives = 0 diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build deleted file mode 100644 index 67aed1e492..0000000000 --- a/tests/qemu-iotests/meson.build +++ /dev/null @@ -1,5 +0,0 @@ -if 'CONFIG_LINUX' in config_host - socket_scm_helper = executable('socket_scm_helper', 'socket_scm_helper.c') -else - socket_scm_helper = [] -endif diff --git a/tests/qemu-iotests/socket_scm_helper.c b/tests/qemu-iotests/socket_scm_helper.c deleted file mode 100644 index eb76d31aa9..0000000000 --- a/tests/qemu-iotests/socket_scm_helper.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * SCM_RIGHTS with unix socket help program for test - * - * Copyright IBM, Inc. 2013 - * - * Authors: - * Wenchao Xia - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include -#include - -/* #define SOCKET_SCM_DEBUG */ - -/* - * @fd and @fd_to_send will not be checked for validation in this function, - * a blank will be sent as iov data to notify qemu. - */ -static int send_fd(int fd, int fd_to_send) -{ - struct msghdr msg; - struct iovec iov[1]; - int ret; - char control[CMSG_SPACE(sizeof(int))]; - struct cmsghdr *cmsg; - - memset(&msg, 0, sizeof(msg)); - memset(control, 0, sizeof(control)); - - /* Send a blank to notify qemu */ - iov[0].iov_base = (void *)" "; - iov[0].iov_len = 1; - - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - cmsg = CMSG_FIRSTHDR(&msg); - - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(int)); - - do { - ret = sendmsg(fd, &msg, 0); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) { - fprintf(stderr, "Failed to send msg, reason: %s\n", strerror(errno)); - } - - return ret; -} - -/* Convert string to fd number. */ -static int get_fd_num(const char *fd_str, bool silent) -{ - int sock; - char *err; - - errno = 0; - sock = strtol(fd_str, &err, 10); - if (errno) { - if (!silent) { - fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n", - strerror(errno)); - } - return -1; - } - if (!*fd_str || *err || sock < 0) { - if (!silent) { - fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str); - } - return -1; - } - - return sock; -} - -/* - * To make things simple, the caller needs to specify: - * 1. socket fd. - * 2. path of the file to be sent. - */ -int main(int argc, char **argv, char **envp) -{ - int sock, fd, ret; - -#ifdef SOCKET_SCM_DEBUG - int i; - for (i = 0; i < argc; i++) { - fprintf(stderr, "Parameter %d: %s\n", i, argv[i]); - } -#endif - - if (argc != 3) { - fprintf(stderr, - "Usage: %s < socket-fd > < file-path >\n", - argv[0]); - return EXIT_FAILURE; - } - - - sock = get_fd_num(argv[1], false); - if (sock < 0) { - return EXIT_FAILURE; - } - - fd = get_fd_num(argv[2], true); - if (fd < 0) { - /* Now only open a file in readonly mode for test purpose. If more - precise control is needed, use python script in file operation, which - is supposed to fork and exec this program. */ - fd = open(argv[2], O_RDONLY); - if (fd < 0) { - fprintf(stderr, "Failed to open file '%s'\n", argv[2]); - return EXIT_FAILURE; - } - } - - ret = send_fd(sock, fd); - if (ret < 0) { - close(fd); - return EXIT_FAILURE; - } - - close(fd); - return EXIT_SUCCESS; -} diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index 99a57a69f3..c33454fa68 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -68,7 +68,7 @@ class TestEnv(ContextManager['TestEnv']): env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR', 'OUTPUT_DIR', 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG', 'QEMU_IO_PROG', 'QEMU_NBD_PROG', 'QSD_PROG', - 'SOCKET_SCM_HELPER', 'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS', + 'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS', 'QEMU_IO_OPTIONS', 'QEMU_IO_OPTIONS_NO_FMT', 'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO', 'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU', @@ -140,7 +140,6 @@ class TestEnv(ContextManager['TestEnv']): """Init binary path variables: PYTHON (for bash tests) QEMU_PROG, QEMU_IMG_PROG, QEMU_IO_PROG, QEMU_NBD_PROG, QSD_PROG - SOCKET_SCM_HELPER """ self.python = sys.executable @@ -174,10 +173,6 @@ class TestEnv(ContextManager['TestEnv']): if not isxfile(b): sys.exit('Not executable: ' + b) - helper_path = os.path.join(self.build_iotests, 'socket_scm_helper') - if isxfile(helper_path): - self.socket_scm_helper = helper_path # SOCKET_SCM_HELPER - def __init__(self, imgfmt: str, imgproto: str, aiomode: str, cachemode: Optional[str] = None, imgopts: Optional[str] = None, @@ -303,7 +298,6 @@ IMGPROTO -- {IMGPROTO} PLATFORM -- {platform} TEST_DIR -- {TEST_DIR} SOCK_DIR -- {SOCK_DIR} -SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER} GDB_OPTIONS -- {GDB_OPTIONS} VALGRIND_QEMU -- {VALGRIND_QEMU} PRINT_QEMU_OUTPUT -- {PRINT_QEMU}