qmp: add utility to execute hmp commands

Mostly for ease of use and later usage.
This commit is contained in:
Lily Tsuru 2024-05-09 03:36:09 -04:00
parent 0e017cd00d
commit c4caa5f643
3 changed files with 30 additions and 3 deletions

View file

@ -25,6 +25,9 @@ async fn main() {
println!("Connected to QMP server"); 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's try to get all STOP events from QMP now.
let mut rx = client.subscribe_to_event("STOP".into()).await.unwrap(); let mut rx = client.subscribe_to_event("STOP".into()).await.unwrap();

View file

@ -217,8 +217,7 @@ where
let (tx, rx) = mpsc::channel(8); let (tx, rx) = mpsc::channel(8);
let inner = QmpClientActor::new(socket, rx); let inner = QmpClientActor::new(socket, rx);
tokio::spawn(async move { tokio::spawn(async move {
let res = inner.run().await; inner.run().await.inspect_err(|err| {
res.inspect_err(|err| {
tracing::error!("error in actor runloop: {err}"); tracing::error!("error in actor runloop: {err}");
}); });
}); });

View file

@ -1,5 +1,6 @@
//! QMP client. //! QMP client.
use serde_json::json;
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
sync::{mpsc, oneshot}, sync::{mpsc, oneshot},
@ -35,6 +36,16 @@ impl QmpClient {
// TODO handle errors properly // 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<types::QmpHandshake> { pub async fn handshake(&self) -> result::Result<types::QmpHandshake> {
let (tx, rx) = oneshot::channel(); 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<serde_json::Value>) -> result::Result<serde_json::Value> { pub async fn execute(&self, command: String, arguments: Option<serde_json::Value>) -> result::Result<serde_json::Value> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
@ -62,7 +76,18 @@ impl QmpClient {
Err(err) => Err(result::QmpError::GeneralFail), 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<String> {
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 /// Returns the recvieving arm of a [mpsc::channel] which will recieve
/// the JSON event payloads when an event is sent by the server. /// the JSON event payloads when an event is sent by the server.
pub async fn subscribe_to_event( pub async fn subscribe_to_event(