Skip to content

Commit

Permalink
hypestv: add force flag to kill command (microsoft#393)
Browse files Browse the repository at this point in the history
Normally, `kill` will power off the VM using `hvc.exe`, which uses the
VMMS WMI API internally. In some cases, the VMMS thinks the VM is not in
a state where it can be powered off even though it can be, e.g. if there
is a pending shutdown request.

Add a `force` flag that uses the HCS API instead (via `hcsdiag.exe`),
which skips the VMMS state machine evaluation and just stops the worker
process.
  • Loading branch information
jstarks authored Nov 24, 2024
1 parent a555dff commit dab9fd2
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 4 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ dependencies = [
"anstyle",
"clap_lex",
"strsim",
"terminal_size",
]

[[package]]
Expand Down Expand Up @@ -6157,6 +6158,16 @@ dependencies = [
"winapi-util",
]

[[package]]
name = "terminal_size"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
dependencies = [
"rustix",
"windows-sys 0.48.0",
]

[[package]]
name = "test_with_tracing"
version = "0.0.0"
Expand Down
2 changes: 1 addition & 1 deletion hyperv/tools/hypestv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mesh.workspace = true
pal_async.workspace = true

anyhow.workspace = true
clap.workspace = true
clap = { workspace = true, features = ["wrap_help"] }
dirs.workspace = true
futures.workspace = true
futures-concurrency.workspace = true
Expand Down
13 changes: 13 additions & 0 deletions hyperv/tools/hypestv/src/windows/hyperv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@
use anyhow::Context as _;

/// Runs hcsdiag with the given arguments.
pub fn run_hcsdiag(
f: impl FnOnce(&mut std::process::Command) -> &mut std::process::Command,
) -> anyhow::Result<()> {
let mut cmd = std::process::Command::new("hcsdiag.exe");
f(&mut cmd);
let status = cmd.status().context("failed to launch hcsdiag")?;
if !status.success() {
anyhow::bail!("hcsdiag failed with exit code: {}", status);
}
Ok(())
}

/// Runs hvc with the given arguments.
pub fn run_hvc(
f: impl FnOnce(&mut std::process::Command) -> &mut std::process::Command,
Expand Down
11 changes: 10 additions & 1 deletion hyperv/tools/hypestv/src/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use vm::Vm;
disable_help_flag = true,
disable_version_flag = true,
no_binary_name = true,
max_term_width = 100,
help_template("{subcommands}")
)]
pub(crate) enum InteractiveCommand {
Expand Down Expand Up @@ -62,7 +63,15 @@ pub(crate) enum VmCommand {
},

/// Power off the VM.
Kill,
Kill {
/// Force powering off the VM via the HCS API.
///
/// Without this flag, this command uses the Hyper-V WMI interface.
/// This may fail if the VM is in a transition state that prevents
/// powering off for whatever reason (usually due to Hyper-V bugs).
#[clap(short, long)]
force: bool,
},

/// Reset the VM.
Reset,
Expand Down
9 changes: 7 additions & 2 deletions hyperv/tools/hypestv/src/windows/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! VM command handling.
use super::hyperv::hvc_output;
use super::hyperv::run_hcsdiag;
use super::hyperv::run_hvc;
use super::rustyline_printer::Printer;
use super::InspectTarget;
Expand Down Expand Up @@ -111,8 +112,12 @@ impl Vm {
})
}
}
VmCommand::Kill => self.delay(move |inner| {
run_hvc(|cmd| cmd.arg("kill").arg(&inner.name))?;
VmCommand::Kill { force } => self.delay(move |inner| {
if force {
run_hcsdiag(|cmd| cmd.arg("kill").arg(inner.id.to_string()))?;
} else {
run_hvc(|cmd| cmd.arg("kill").arg(&inner.name))?;
}
writeln!(inner.printer.out(), "VM killed")?;
Ok(())
}),
Expand Down

0 comments on commit dab9fd2

Please sign in to comment.