diff --git a/monitor/src/mmp.rs b/monitor/src/mmp.rs index c9b4be5f..26c0c909 100644 --- a/monitor/src/mmp.rs +++ b/monitor/src/mmp.rs @@ -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; } } @@ -130,6 +130,13 @@ pub fn dispatch(cmd: &str, svc: &Arc>) -> 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(); + json!({"return": {}}) + } "query-cpus-fast" => { let cpus = s.query_cpus(); let arr: Vec = cpus diff --git a/tests/src/monitor.rs b/tests/src/monitor.rs index 67f25058..8dfae9a1 100644 --- a/tests/src/monitor.rs +++ b/tests/src/monitor.rs @@ -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]