Skip to content

Commit

Permalink
python: introduce qmp-shell-wrap convenience tool
Browse files Browse the repository at this point in the history
With the current 'qmp-shell' tool developers must first spawn QEMU with
a suitable -qmp arg and then spawn qmp-shell in a separate terminal
pointing to the right socket.

With 'qmp-shell-wrap' developers can ignore QMP sockets entirely and
just pass the QEMU command and arguments they want. The program will
listen on a UNIX socket and tell QEMU to connect QMP to that.

For example, this:

 # qmp-shell-wrap -- qemu-system-x86_64 -display none

Is roughly equivalent of running:

 # qemu-system-x86_64 -display none -qmp qmp-shell-1234 &
 # qmp-shell qmp-shell-1234

Except that 'qmp-shell-wrap' switches the socket peers around so that
it is the UNIX socket server and QEMU is the socket client. This makes
QEMU reliably go away when qmp-shell-wrap exits, closing the server
socket.

Signed-off-by: Daniel P. Berrangé <[email protected]>
Message-id: [email protected]
[Edited for rebase. --js]
Signed-off-by: John Snow <[email protected]>
  • Loading branch information
berrange authored and jnsnow committed Feb 23, 2022
1 parent 31e3caf commit 4391252
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 4 deletions.
65 changes: 61 additions & 4 deletions python/qemu/aqmp/qmp_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
import os
import re
import readline
from subprocess import Popen
import sys
from typing import (
Iterator,
Expand Down Expand Up @@ -167,8 +168,10 @@ class QMPShell(QEMUMonitorProtocol):
:param verbose: Echo outgoing QMP messages to console.
"""
def __init__(self, address: SocketAddrT,
pretty: bool = False, verbose: bool = False):
super().__init__(address)
pretty: bool = False,
verbose: bool = False,
server: bool = False):
super().__init__(address, server=server)
self._greeting: Optional[QMPMessage] = None
self._completer = QMPCompleter()
self._transmode = False
Expand Down Expand Up @@ -409,8 +412,10 @@ class HMPShell(QMPShell):
:param verbose: Echo outgoing QMP messages to console.
"""
def __init__(self, address: SocketAddrT,
pretty: bool = False, verbose: bool = False):
super().__init__(address, pretty, verbose)
pretty: bool = False,
verbose: bool = False,
server: bool = False):
super().__init__(address, pretty, verbose, server)
self._cpu_index = 0

def _cmd_completion(self) -> None:
Expand Down Expand Up @@ -533,5 +538,57 @@ def main() -> None:
pass


def main_wrap() -> None:
"""
qmp-shell-wrap entry point: parse command line arguments and
start the REPL.
"""
parser = argparse.ArgumentParser()
parser.add_argument('-H', '--hmp', action='store_true',
help='Use HMP interface')
parser.add_argument('-v', '--verbose', action='store_true',
help='Verbose (echo commands sent and received)')
parser.add_argument('-p', '--pretty', action='store_true',
help='Pretty-print JSON')

parser.add_argument('command', nargs=argparse.REMAINDER,
help='QEMU command line to invoke')

args = parser.parse_args()

cmd = args.command
if len(cmd) != 0 and cmd[0] == '--':
cmd = cmd[1:]
if len(cmd) == 0:
cmd = ["qemu-system-x86_64"]

sockpath = "qmp-shell-wrap-%d" % os.getpid()
cmd += ["-qmp", "unix:%s" % sockpath]

shell_class = HMPShell if args.hmp else QMPShell

try:
address = shell_class.parse_address(sockpath)
except QMPBadPortError:
parser.error(f"Bad port number: {sockpath}")
return # pycharm doesn't know error() is noreturn

try:
with shell_class(address, args.pretty, args.verbose, True) as qemu:
with Popen(cmd):

try:
qemu.accept()
except ConnectError as err:
if isinstance(err.exc, OSError):
die(f"Couldn't connect to {args.qmp_server}: {err!s}")
die(str(err))

for _ in qemu.repl():
pass
finally:
os.unlink(sockpath)


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions python/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ console_scripts =
qom-fuse = qemu.utils.qom_fuse:QOMFuse.entry_point [fuse]
qemu-ga-client = qemu.utils.qemu_ga_client:main
qmp-shell = qemu.aqmp.qmp_shell:main
qmp-shell-wrap = qemu.aqmp.qmp_shell:main_wrap
aqmp-tui = qemu.aqmp.aqmp_tui:main [tui]

[flake8]
Expand Down
11 changes: 11 additions & 0 deletions scripts/qmp/qmp-shell-wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env python3

import os
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu.qmp import qmp_shell


if __name__ == '__main__':
qmp_shell.main_wrap()

0 comments on commit 4391252

Please sign in to comment.