From c4caa5f6431b6cce4bd7670c9f218e082c4998c8 Mon Sep 17 00:00:00 2001 From: modeco80 Date: Thu, 9 May 2024 03:36:09 -0400 Subject: [PATCH] qmp: add utility to execute hmp commands Mostly for ease of use and later usage. --- src/main.rs | 3 +++ src/qmp/actor.rs | 3 +-- src/qmp/client.rs | 27 ++++++++++++++++++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 733d560..b3bb4fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,9 @@ async fn main() { println!("Connected to QMP server"); + + println!("res {}", client.execute_hmp("info block".into()).await.expect("this shouldn't fail")); + // let's try to get all STOP events from QMP now. let mut rx = client.subscribe_to_event("STOP".into()).await.unwrap(); diff --git a/src/qmp/actor.rs b/src/qmp/actor.rs index 8d02f76..ab3a889 100644 --- a/src/qmp/actor.rs +++ b/src/qmp/actor.rs @@ -217,8 +217,7 @@ where let (tx, rx) = mpsc::channel(8); let inner = QmpClientActor::new(socket, rx); tokio::spawn(async move { - let res = inner.run().await; - res.inspect_err(|err| { + inner.run().await.inspect_err(|err| { tracing::error!("error in actor runloop: {err}"); }); }); diff --git a/src/qmp/client.rs b/src/qmp/client.rs index 1b7f587..b91d06f 100644 --- a/src/qmp/client.rs +++ b/src/qmp/client.rs @@ -1,5 +1,6 @@ //! QMP client. +use serde_json::json; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, sync::{mpsc, oneshot}, @@ -35,6 +36,16 @@ impl QmpClient { // TODO handle errors properly + /// Closes this client. + pub async fn close(&self) -> result::Result<()> { + let _ = self + .tx + .send(actor::QmpActorMessage::QmpDoClose { }) + .await; + Ok(()) + } + + /// Handshakes QMP with the server. This **MUST** be the first operation you do. pub async fn handshake(&self) -> result::Result { let (tx, rx) = oneshot::channel(); @@ -48,7 +59,10 @@ impl QmpClient { } } + // FIXME: QMP errors should bubble out into an Rust error, + // for now this isn't done. + /// Executes a single QMP command. pub async fn execute(&self, command: String, arguments: Option) -> result::Result { let (tx, rx) = oneshot::channel(); @@ -62,7 +76,18 @@ impl QmpClient { Err(err) => Err(result::QmpError::GeneralFail), } } - /// Subscribes to a QMP event. + + /// Executes a HMP (QEMU Monitor) command. + pub async fn execute_hmp(&self, hmp_line: String) -> result::Result { + let res = self.execute("human-monitor-command".into(), Some(json!({ + "command-line": hmp_line + }))).await?; + + // This is very nasty, but this is always a string. + Ok(String::from(res["return"].as_str().unwrap())) + } + + /// Subscribes to a QMP event with the given event name. /// Returns the recvieving arm of a [mpsc::channel] which will recieve /// the JSON event payloads when an event is sent by the server. pub async fn subscribe_to_event(