1use anyhow::{Context, Result};
2use std::path::Path;
3use std::process::{Command, Output, Stdio};
4
5use crate::config::VM_NAME;
6use mvm_core::platform;
7
8pub fn run_host(cmd: &str, args: &[&str]) -> Result<Output> {
10 Command::new(cmd)
11 .args(args)
12 .output()
13 .with_context(|| format!("Failed to run: {} {}", cmd, args.join(" ")))
14}
15
16pub fn run_host_visible(cmd: &str, args: &[&str]) -> Result<()> {
18 let status = Command::new(cmd)
19 .args(args)
20 .stdin(Stdio::inherit())
21 .stdout(Stdio::inherit())
22 .stderr(Stdio::inherit())
23 .status()
24 .with_context(|| format!("Failed to run: {} {}", cmd, args.join(" ")))?;
25
26 if !status.success() {
27 anyhow::bail!(
28 "Command failed (exit {}): {} {}",
29 status.code().unwrap_or(-1),
30 cmd,
31 args.join(" ")
32 );
33 }
34 Ok(())
35}
36
37pub fn run_on_vm(vm_name: &str, script: &str) -> Result<Output> {
42 if let Some(output) = crate::shell_mock::intercept(script) {
43 return Ok(output);
44 }
45
46 if platform::current().needs_lima() {
47 Command::new("limactl")
48 .args(["shell", vm_name, "bash", "-c", script])
49 .output()
50 .with_context(|| format!("Failed to run command in Lima VM '{}'", vm_name))
51 } else {
52 Command::new("bash")
53 .args(["-c", script])
54 .output()
55 .with_context(|| "Failed to run command on host")
56 }
57}
58
59pub fn run_on_vm_visible(vm_name: &str, script: &str) -> Result<()> {
61 let status = if platform::current().needs_lima() {
62 Command::new("limactl")
63 .args(["shell", vm_name, "bash", "-c", script])
64 .stdin(Stdio::inherit())
65 .stdout(Stdio::inherit())
66 .stderr(Stdio::inherit())
67 .status()
68 .with_context(|| format!("Failed to run command in Lima VM '{}'", vm_name))?
69 } else {
70 Command::new("bash")
71 .args(["-c", script])
72 .stdin(Stdio::inherit())
73 .stdout(Stdio::inherit())
74 .stderr(Stdio::inherit())
75 .status()
76 .with_context(|| "Failed to run command on host")?
77 };
78
79 if !status.success() {
80 if platform::current().needs_lima() {
81 anyhow::bail!(
82 "Command failed in Lima VM '{}' (exit {})",
83 vm_name,
84 status.code().unwrap_or(-1)
85 );
86 } else {
87 anyhow::bail!("Command failed (exit {})", status.code().unwrap_or(-1));
88 }
89 }
90 Ok(())
91}
92
93pub fn run_on_vm_stdout(vm_name: &str, script: &str) -> Result<String> {
95 let output = run_on_vm(vm_name, script)?;
96 Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
97}
98
99pub fn run_in_vm(script: &str) -> Result<Output> {
101 run_on_vm(VM_NAME, script)
102}
103
104pub fn run_in_vm_visible(script: &str) -> Result<()> {
106 run_on_vm_visible(VM_NAME, script)
107}
108
109pub fn run_in_vm_stdout(script: &str) -> Result<String> {
111 run_on_vm_stdout(VM_NAME, script)
112}
113
114pub fn inside_lima() -> bool {
117 std::env::var("LIMA_INSTANCE").is_ok()
118 || Path::new("/etc/lima-boot.conf").exists()
119 || Path::new("/run/lima-guestagent.sock").exists()
120}
121
122#[cfg(unix)]
126pub fn replace_process(cmd: &str, args: &[&str]) -> Result<()> {
127 use std::os::unix::process::CommandExt;
128
129 let err = Command::new(cmd)
130 .args(args)
131 .stdin(Stdio::inherit())
132 .stdout(Stdio::inherit())
133 .stderr(Stdio::inherit())
134 .exec();
135
136 Err(err).with_context(|| format!("Failed to exec: {} {}", cmd, args.join(" ")))
138}