Pull request
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+ber27ys35W+dsvQfe+BBqr8OQ4FAmFl/8cACgkQfe+BBqr8 OQ7I8A/+K1vF5/TYME+9jPGJFrVVrT4TgMbzQlwxy6WOPXdO1prGmBR5cu9EqyfM PcQ4meYqKMGIWOYcxVYpDcsTkzT94nc4VuMQx0b3jw9W6PIKd4zZd0Kt10FqepAE akr8dzcgsClVvSwBHo9/9fXT5WWLGdeCttR42Lpv25ggMKSLuiL51w+/TmxJ8zuU pUn7tq2AjlQugqkZm+qtSq18Lu/0trmtPH5FyMCs0xxAPYP/h0QwaT6DizYIsLdz xZDH7ds/VsnQ41l5d+xG2/uh55ZTSzGQrCyVGoDbSI4jR8mWh3nj5uj1FQLWfKHT z7k/0oQZpp+9yFJ7M+yvPtW9L/narWuxW33qgGKT+CpicmEz1ZcEt9erWVitwbB9 diP2z3AZzZlbxSOf+QKzl4TgYaKmFXcR6tCleitdvkwbUvOeSu0DyqmgMikdiRQw 04BN3deuPjcZPWB007vQ/hQXdt2rQdv9E+E0qLszDyFBIVE2WIR4BNRl4AmsaSgz Fj8JrAERtdlaTW/iO34wboob3k+6e8de+USbqEMj1SgQBbsYRy5SGW0WaOe9r3lV D0ywZ8nB9G2Bb4iAvY38S80c04bAkgYwZuA4fpVjT0NpdLB0IcOBxLTuGwFrgfOF CBZ0e69pIMouWvRS56WPFUd6rXFCLtI0nXqiHxR/wunprB0/krU= =Yxqp -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/jsnow/tags/python-pull-request' into staging Pull request # gpg: Signature made Tue 12 Oct 2021 02:36:07 PM PDT # gpg: using RSA key F9B7ABDBBCACDF95BE76CBD07DEF8106AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" [full] * remotes/jsnow/tags/python-pull-request: python, iotests: remove socket_scm_helper python/qmp: add send_fd_scm directly to QEMUMonitorProtocol python/qmp: clear events on get_events() call python/aqmp: Disable logging messages by default python/aqmp: Reduce severity of EOFError-caused loop terminations python/aqmp: Add dict conversion method to Greeting object python/aqmp: add send_fd_scm python/aqmp: Return cleared events from EventListener.clear() python/aqmp: add .empty() method to EventListener python/aqmp: add greeting property to QMPClient Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
ee26ce674a
|
@ -21,6 +21,7 @@ managing QMP events.
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2. See
|
# This work is licensed under the terms of the GNU GPL, version 2. See
|
||||||
# the COPYING file in the top-level directory.
|
# the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from .error import AQMPError
|
from .error import AQMPError
|
||||||
|
@ -41,6 +42,9 @@ Proceed with caution!
|
||||||
|
|
||||||
warnings.warn(_WMSG, FutureWarning)
|
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.
|
# The order of these fields impact the Sphinx documentation order.
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
|
|
@ -556,7 +556,13 @@ class EventListener:
|
||||||
"""
|
"""
|
||||||
return await self._queue.get()
|
return await self._queue.get()
|
||||||
|
|
||||||
def clear(self) -> None:
|
def empty(self) -> bool:
|
||||||
|
"""
|
||||||
|
Return `True` if there are no pending events.
|
||||||
|
"""
|
||||||
|
return self._queue.empty()
|
||||||
|
|
||||||
|
def clear(self) -> List[Message]:
|
||||||
"""
|
"""
|
||||||
Clear this listener of all pending events.
|
Clear this listener of all pending events.
|
||||||
|
|
||||||
|
@ -564,17 +570,22 @@ class EventListener:
|
||||||
pending FIFO queue synchronously. It can be also be used to
|
pending FIFO queue synchronously. It can be also be used to
|
||||||
manually clear any pending events, if desired.
|
manually clear any pending events, if desired.
|
||||||
|
|
||||||
|
:return: The cleared events, if any.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Take care when discarding events. Cleared events will be
|
Take care when discarding events. Cleared events will be
|
||||||
silently tossed on the floor. All events that were ever
|
silently tossed on the floor. All events that were ever
|
||||||
accepted by this listener are visible in `history()`.
|
accepted by this listener are visible in `history()`.
|
||||||
"""
|
"""
|
||||||
|
events = []
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self._queue.get_nowait()
|
events.append(self._queue.get_nowait())
|
||||||
except asyncio.QueueEmpty:
|
except asyncio.QueueEmpty:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
return events
|
||||||
|
|
||||||
def __aiter__(self) -> AsyncIterator[Message]:
|
def __aiter__(self) -> AsyncIterator[Message]:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,10 @@ data to make sure it conforms to spec.
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
from collections import abc
|
from collections import abc
|
||||||
|
import copy
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
|
Dict,
|
||||||
Mapping,
|
Mapping,
|
||||||
Optional,
|
Optional,
|
||||||
Sequence,
|
Sequence,
|
||||||
|
@ -66,6 +68,17 @@ class Greeting(Model):
|
||||||
self._check_member('QMP', abc.Mapping, "JSON object")
|
self._check_member('QMP', abc.Mapping, "JSON object")
|
||||||
self.QMP = QMPGreeting(self._raw['QMP'])
|
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):
|
class QMPGreeting(Model):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -721,8 +721,11 @@ class AsyncProtocol(Generic[T]):
|
||||||
self.logger.debug("Task.%s: cancelled.", name)
|
self.logger.debug("Task.%s: cancelled.", name)
|
||||||
return
|
return
|
||||||
except BaseException as err:
|
except BaseException as err:
|
||||||
self.logger.error("Task.%s: %s",
|
self.logger.log(
|
||||||
name, exception_summary(err))
|
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",
|
self.logger.debug("Task.%s: failure:\n%s\n",
|
||||||
name, pretty_traceback())
|
name, pretty_traceback())
|
||||||
self._schedule_disconnect()
|
self._schedule_disconnect()
|
||||||
|
|
|
@ -9,6 +9,8 @@ accept an incoming connection from that server.
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
from typing import (
|
from typing import (
|
||||||
Dict,
|
Dict,
|
||||||
List,
|
List,
|
||||||
|
@ -224,6 +226,11 @@ class QMPClient(AsyncProtocol[Message], Events):
|
||||||
'asyncio.Queue[QMPClient._PendingT]'
|
'asyncio.Queue[QMPClient._PendingT]'
|
||||||
] = {}
|
] = {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def greeting(self) -> Optional[Greeting]:
|
||||||
|
"""The `Greeting` from the QMP server, if any."""
|
||||||
|
return self._greeting
|
||||||
|
|
||||||
@upper_half
|
@upper_half
|
||||||
async def _establish_session(self) -> None:
|
async def _establish_session(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -619,3 +626,23 @@ class QMPClient(AsyncProtocol[Message], Events):
|
||||||
"""
|
"""
|
||||||
msg = self.make_execute_msg(cmd, arguments, oob=oob)
|
msg = self.make_execute_msg(cmd, arguments, oob=oob)
|
||||||
return await self.execute_msg(msg)
|
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))]
|
||||||
|
)
|
||||||
|
|
|
@ -98,7 +98,6 @@ class QEMUMachine:
|
||||||
name: Optional[str] = None,
|
name: Optional[str] = None,
|
||||||
base_temp_dir: str = "/var/tmp",
|
base_temp_dir: str = "/var/tmp",
|
||||||
monitor_address: Optional[SocketAddrT] = None,
|
monitor_address: Optional[SocketAddrT] = None,
|
||||||
socket_scm_helper: Optional[str] = None,
|
|
||||||
sock_dir: Optional[str] = None,
|
sock_dir: Optional[str] = None,
|
||||||
drain_console: bool = False,
|
drain_console: bool = False,
|
||||||
console_log: Optional[str] = None,
|
console_log: Optional[str] = None,
|
||||||
|
@ -113,7 +112,6 @@ class QEMUMachine:
|
||||||
@param name: prefix for socket and log file names (default: qemu-PID)
|
@param name: prefix for socket and log file names (default: qemu-PID)
|
||||||
@param base_temp_dir: default location where temp files are created
|
@param base_temp_dir: default location where temp files are created
|
||||||
@param monitor_address: address for QMP monitor
|
@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 sock_dir: where to create socket (defaults to base_temp_dir)
|
||||||
@param drain_console: (optional) True to drain console socket to buffer
|
@param drain_console: (optional) True to drain console socket to buffer
|
||||||
@param console_log: (optional) path to console log file
|
@param console_log: (optional) path to console log file
|
||||||
|
@ -134,7 +132,6 @@ class QEMUMachine:
|
||||||
self._base_temp_dir = base_temp_dir
|
self._base_temp_dir = base_temp_dir
|
||||||
self._sock_dir = sock_dir or self._base_temp_dir
|
self._sock_dir = sock_dir or self._base_temp_dir
|
||||||
self._log_dir = log_dir
|
self._log_dir = log_dir
|
||||||
self._socket_scm_helper = socket_scm_helper
|
|
||||||
|
|
||||||
if monitor_address is not None:
|
if monitor_address is not None:
|
||||||
self._monitor_address = monitor_address
|
self._monitor_address = monitor_address
|
||||||
|
@ -213,48 +210,22 @@ class QEMUMachine:
|
||||||
def send_fd_scm(self, fd: Optional[int] = None,
|
def send_fd_scm(self, fd: Optional[int] = None,
|
||||||
file_path: Optional[str] = None) -> int:
|
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.
|
Exactly one of fd and file_path must be given. If it is
|
||||||
If it is file_path, the helper will open that file and pass its own fd.
|
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:
|
if file_path is not None:
|
||||||
assert fd is 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:
|
else:
|
||||||
assert fd is not None
|
assert fd is not None
|
||||||
fd_param.append(str(fd))
|
self._qmp.send_fd_scm(fd)
|
||||||
|
|
||||||
proc = subprocess.run(
|
return 0
|
||||||
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
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _remove_if_exists(path: str) -> None:
|
def _remove_if_exists(path: str) -> None:
|
||||||
|
@ -631,7 +602,6 @@ class QEMUMachine:
|
||||||
events = self._qmp.get_events(wait=wait)
|
events = self._qmp.get_events(wait=wait)
|
||||||
events.extend(self._events)
|
events.extend(self._events)
|
||||||
del self._events[:]
|
del self._events[:]
|
||||||
self._qmp.clear_events()
|
|
||||||
return events
|
return events
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -115,7 +115,6 @@ class QEMUQtestMachine(QEMUMachine):
|
||||||
wrapper: Sequence[str] = (),
|
wrapper: Sequence[str] = (),
|
||||||
name: Optional[str] = None,
|
name: Optional[str] = None,
|
||||||
base_temp_dir: str = "/var/tmp",
|
base_temp_dir: str = "/var/tmp",
|
||||||
socket_scm_helper: Optional[str] = None,
|
|
||||||
sock_dir: Optional[str] = None,
|
sock_dir: Optional[str] = None,
|
||||||
qmp_timer: Optional[float] = None):
|
qmp_timer: Optional[float] = None):
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
|
@ -126,7 +125,6 @@ class QEMUQtestMachine(QEMUMachine):
|
||||||
sock_dir = base_temp_dir
|
sock_dir = base_temp_dir
|
||||||
super().__init__(binary, args, wrapper=wrapper, name=name,
|
super().__init__(binary, args, wrapper=wrapper, name=name,
|
||||||
base_temp_dir=base_temp_dir,
|
base_temp_dir=base_temp_dir,
|
||||||
socket_scm_helper=socket_scm_helper,
|
|
||||||
sock_dir=sock_dir, qmp_timer=qmp_timer)
|
sock_dir=sock_dir, qmp_timer=qmp_timer)
|
||||||
self._qtest: Optional[QEMUQtestProtocol] = None
|
self._qtest: Optional[QEMUQtestProtocol] = None
|
||||||
self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock")
|
self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock")
|
||||||
|
|
|
@ -21,6 +21,7 @@ import errno
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
import struct
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
|
@ -361,7 +362,7 @@ class QEMUMonitorProtocol:
|
||||||
|
|
||||||
def get_events(self, wait: bool = False) -> List[QMPMessage]:
|
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 (bool): block until an event is available.
|
||||||
@param wait (float): If wait is a float, treat it as a timeout value.
|
@param wait (float): If wait is a float, treat it as a timeout value.
|
||||||
|
@ -374,7 +375,9 @@ class QEMUMonitorProtocol:
|
||||||
@return The list of available QMP events.
|
@return The list of available QMP events.
|
||||||
"""
|
"""
|
||||||
self.__get_events(wait)
|
self.__get_events(wait)
|
||||||
return self.__events
|
events = self.__events
|
||||||
|
self.__events = []
|
||||||
|
return events
|
||||||
|
|
||||||
def clear_events(self) -> None:
|
def clear_events(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -406,18 +409,14 @@ class QEMUMonitorProtocol:
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
self.__sock.settimeout(timeout)
|
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.
|
self.__sock.sendmsg(
|
||||||
"""
|
[b' '],
|
||||||
return self.__sock.fileno()
|
[(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))]
|
||||||
|
)
|
||||||
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
|
|
||||||
|
|
|
@ -381,7 +381,6 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||||
if cmdline == '':
|
if cmdline == '':
|
||||||
for event in self.get_events():
|
for event in self.get_events():
|
||||||
print(event)
|
print(event)
|
||||||
self.clear_events()
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return self._execute_cmd(cmdline)
|
return self._execute_cmd(cmdline)
|
||||||
|
|
|
@ -148,7 +148,6 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images
|
||||||
check:
|
check:
|
||||||
|
|
||||||
ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy)
|
ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy)
|
||||||
QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXESUF)
|
|
||||||
check: check-block
|
check: check-block
|
||||||
export PYTHON
|
export PYTHON
|
||||||
check-block: $(SRC_PATH)/tests/check-block.sh qemu-img$(EXESUF) \
|
check-block: $(SRC_PATH)/tests/check-block.sh qemu-img$(EXESUF) \
|
||||||
|
|
|
@ -67,10 +67,6 @@ if have_tools and 'CONFIG_VHOST_USER' in config_host and 'CONFIG_LINUX' in confi
|
||||||
dependencies: [qemuutil, vhost_user])
|
dependencies: [qemuutil, vhost_user])
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if have_system and 'CONFIG_POSIX' in config_host
|
|
||||||
subdir('qemu-iotests')
|
|
||||||
endif
|
|
||||||
|
|
||||||
test('decodetree', sh,
|
test('decodetree', sh,
|
||||||
args: [ files('decode/check.sh'), config_host['PYTHON'], files('../scripts/decodetree.py') ],
|
args: [ files('decode/check.sh'), config_host['PYTHON'], files('../scripts/decodetree.py') ],
|
||||||
workdir: meson.current_source_dir() / 'decode',
|
workdir: meson.current_source_dir() / 'decode',
|
||||||
|
|
|
@ -107,8 +107,6 @@ if os.environ.get('VALGRIND_QEMU') == "y" and \
|
||||||
|
|
||||||
qemu_valgrind = ['valgrind', valgrind_logfile, '--error-exitcode=99']
|
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=' + \
|
luks_default_secret_object = 'secret,id=keysec0,data=' + \
|
||||||
os.environ.get('IMGKEYSECRET', '')
|
os.environ.get('IMGKEYSECRET', '')
|
||||||
luks_default_key_secret_opt = 'key-secret=keysec0'
|
luks_default_key_secret_opt = 'key-secret=keysec0'
|
||||||
|
@ -598,7 +596,6 @@ class VM(qtest.QEMUQtestMachine):
|
||||||
super().__init__(qemu_prog, qemu_opts, wrapper=wrapper,
|
super().__init__(qemu_prog, qemu_opts, wrapper=wrapper,
|
||||||
name=name,
|
name=name,
|
||||||
base_temp_dir=test_dir,
|
base_temp_dir=test_dir,
|
||||||
socket_scm_helper=socket_scm_helper,
|
|
||||||
sock_dir=sock_dir, qmp_timer=timer)
|
sock_dir=sock_dir, qmp_timer=timer)
|
||||||
self._num_drives = 0
|
self._num_drives = 0
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
|
@ -1,136 +0,0 @@
|
||||||
/*
|
|
||||||
* SCM_RIGHTS with unix socket help program for test
|
|
||||||
*
|
|
||||||
* Copyright IBM, Inc. 2013
|
|
||||||
*
|
|
||||||
* Authors:
|
|
||||||
* Wenchao Xia <xiawenc@linux.vnet.ibm.com>
|
|
||||||
*
|
|
||||||
* 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 <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
|
|
||||||
/* #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;
|
|
||||||
}
|
|
|
@ -68,7 +68,7 @@ class TestEnv(ContextManager['TestEnv']):
|
||||||
env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR',
|
env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR',
|
||||||
'OUTPUT_DIR', 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG',
|
'OUTPUT_DIR', 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG',
|
||||||
'QEMU_IO_PROG', 'QEMU_NBD_PROG', 'QSD_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_IO_OPTIONS', 'QEMU_IO_OPTIONS_NO_FMT',
|
||||||
'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO',
|
'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO',
|
||||||
'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
|
'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
|
||||||
|
@ -140,7 +140,6 @@ class TestEnv(ContextManager['TestEnv']):
|
||||||
"""Init binary path variables:
|
"""Init binary path variables:
|
||||||
PYTHON (for bash tests)
|
PYTHON (for bash tests)
|
||||||
QEMU_PROG, QEMU_IMG_PROG, QEMU_IO_PROG, QEMU_NBD_PROG, QSD_PROG
|
QEMU_PROG, QEMU_IMG_PROG, QEMU_IO_PROG, QEMU_NBD_PROG, QSD_PROG
|
||||||
SOCKET_SCM_HELPER
|
|
||||||
"""
|
"""
|
||||||
self.python = sys.executable
|
self.python = sys.executable
|
||||||
|
|
||||||
|
@ -174,10 +173,6 @@ class TestEnv(ContextManager['TestEnv']):
|
||||||
if not isxfile(b):
|
if not isxfile(b):
|
||||||
sys.exit('Not executable: ' + 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,
|
def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
|
||||||
cachemode: Optional[str] = None,
|
cachemode: Optional[str] = None,
|
||||||
imgopts: Optional[str] = None,
|
imgopts: Optional[str] = None,
|
||||||
|
@ -303,7 +298,6 @@ IMGPROTO -- {IMGPROTO}
|
||||||
PLATFORM -- {platform}
|
PLATFORM -- {platform}
|
||||||
TEST_DIR -- {TEST_DIR}
|
TEST_DIR -- {TEST_DIR}
|
||||||
SOCK_DIR -- {SOCK_DIR}
|
SOCK_DIR -- {SOCK_DIR}
|
||||||
SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}
|
|
||||||
GDB_OPTIONS -- {GDB_OPTIONS}
|
GDB_OPTIONS -- {GDB_OPTIONS}
|
||||||
VALGRIND_QEMU -- {VALGRIND_QEMU}
|
VALGRIND_QEMU -- {VALGRIND_QEMU}
|
||||||
PRINT_QEMU_OUTPUT -- {PRINT_QEMU}
|
PRINT_QEMU_OUTPUT -- {PRINT_QEMU}
|
||||||
|
|
Loading…
Reference in New Issue