Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion monitor/src/mmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ fn handle_connection(
if cmd == "qmp_capabilities" {
caps_done = true;
}
if cmd == "quit" {
if cmd == "quit" || cmd == "system_powerdown" {
return true;
}
}
Expand Down Expand Up @@ -130,6 +130,13 @@ pub fn dispatch(cmd: &str, svc: &Arc<Mutex<MonitorService>>) -> Value {
s.quit();
json!({"return": {}})
}
"system_powerdown" => {
// QMP system_powerdown shuts down the VM. machina has no
// separate ACPI shutdown path, so reuse quit() which signals
// the run loop to stop -- semantically equivalent here.
s.quit();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Treat system_powerdown as guest request, not forced quit

dispatch() currently maps system_powerdown directly to quit(), which forces the VM to terminate immediately instead of requesting an in-guest powerdown flow. In QMP, system_powerdown is a soft guest request that may be ignored or delayed, so conflating it with quit can cause management clients that expect graceful shutdown semantics to trigger abrupt exits (and possible guest data loss) once they switch from handling CommandNotFound to using this command.

Useful? React with 👍 / 👎.

json!({"return": {}})
}
"query-cpus-fast" => {
let cpus = s.query_cpus();
let arr: Vec<Value> = cpus
Expand Down
11 changes: 11 additions & 0 deletions tests/src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,17 @@ fn test_mmp_quit() {
assert!(svc.lock().unwrap().state.is_quit_requested());
}

#[test]
fn test_mmp_system_powerdown() {
// QMP system_powerdown shuts down the VM. machina has no separate
// ACPI shutdown path, so dispatch must reuse quit() and return
// the empty success envelope.
let svc = make_svc();
let resp = mmp::dispatch("system_powerdown", &svc);
assert_eq!(resp["return"], serde_json::json!({}));
assert!(svc.lock().unwrap().state.is_quit_requested());
}

// ── HMP tests ───────────────────────────────────────

#[test]
Expand Down
Loading