From 6c355c80d31b5b3bb0cfcaf72575f20c6e3a75e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Sat, 10 Nov 2018 00:00:00 +0000 Subject: [PATCH] qemu-monitor: migrate to Python! Just came across the telnet in the stdlib, and got rid of the ugly expect dependency, nice. Also implement stdin input now that we have a sane language. --- README.adoc | 8 ++++-- download-dependencies | 3 +- qemu-monitor | 66 ++++++++++++++++++++++++++++--------------- 3 files changed, 50 insertions(+), 27 deletions(-) diff --git a/README.adoc b/README.adoc index 9525eb9..7021a84 100644 --- a/README.adoc +++ b/README.adoc @@ -8017,12 +8017,16 @@ or send one command such as `info qtree` and quit the monitor: ./qemu-monitor info qtree .... +or equivalently: + +.... +echo 'info qtree' | ./qemu-monitor +.... + Source: link:qemu-monitor[] `qemu-monitor` uses the `-monitor` QEMU command line option, which makes the monitor listen from a socket. -`qemu-monitor` does not support input from an stdin pipe currently, see comments on the source for rationale. - Alternatively, from text mode: .... diff --git a/download-dependencies b/download-dependencies index defe44b..cde674b 100755 --- a/download-dependencies +++ b/download-dependencies @@ -88,9 +88,9 @@ automake \ bc \ bison \ build-essential \ +ccache \ coreutils \ cpio \ -expect \ flex \ gcc-aarch64-linux-gnu \ gcc-arm-linux-gnueabihf \ @@ -114,7 +114,6 @@ pexpect==4.6.0 \ " if "$gem5"; then pkgs="${pkgs}\ -ccache \ diod \ libgoogle-perftools-dev \ protobuf-compiler \ diff --git a/qemu-monitor b/qemu-monitor index 9ef4aed..a2bcd9d 100755 --- a/qemu-monitor +++ b/qemu-monitor @@ -1,24 +1,44 @@ -#!/usr/bin/env expect -# Ee have to use expect since QEMU 2.12: just piping commands -# into telnet stopped working at that version. -spawn telnet localhost 45454 -set prompt "\n(qemu) " -expect $prompt -if {$argc > 0} { - send "[concat [join $argv " "]]\r" - expect $prompt -} else { - interact -} +#!/usr/bin/env python3 -# In order to treat input from stdin, we would need to differentiate between input from pipe vs terminal. -# For bash we can do it as: -# https://stackoverflow.com/questions/911168/how-to-detect-if-my-shell-script-is-running-through-a-pipe -# but no one knows for Tcl: -# https://stackoverflow.com/questions/43660612/how-to-check-if-stdin-stdout-are-connected-to-a-terminal-in-tcl -# One option would also be to have a bash wrapper that calls this tcl script. -# Related: https://stackoverflow.com/questions/10237872/expect-redirect-stdin -#while {[gets stdin line] > 0} { - #expect $prompt - #send "$line\r" -#} +import os +import sys +import telnetlib + +import common + +prompt = b'\n(qemu) ' + +parser = common.get_argparse({ + 'description': '''\ +Run a command on the QEMU monitor of a running QEMU instance + +If the stdin is a terminal, open an interact shell. Otherwise, +run commands from stdin and quit. +''' +}) +parser.add_argument( + 'command', + help='If given, run this command and quit', + nargs='*', +) +args = common.setup(parser) + +def write_and_read(tn, cmd, prompt): + tn.write(cmd.encode('utf-8')) + return '\n'.join(tn.read_until(prompt).decode('utf-8').splitlines()[1:])[:-len(prompt)] + +with telnetlib.Telnet('localhost', common.qemu_monitor_port) as tn: + # Couldn't disable server echo, so just removing the write for now. + # https://stackoverflow.com/questions/12421799/how-to-disable-telnet-echo-in-python-telnetlib + # sock = tn.get_socket() + # sock.send(telnetlib.IAC + telnetlib.WILL + telnetlib.ECHO) + if os.isatty(sys.stdin.fileno()): + if args.command == []: + print(tn.read_until(prompt).decode('utf-8'), end='') + tn.interact() + else: + tn.read_until(prompt) + print(write_and_read(tn, ' '.join(args.command) + '\n', prompt)) + else: + tn.read_until(prompt) + print(write_and_read(tn, sys.stdin.read() + '\n', prompt))