Prepare, configure, and manage Firecracker microVMs in seconds!
virtualization
linux
microvm
firecracker
1use anyhow::{anyhow, Context, Error, Result};
2use owo_colors::OwoColorize;
3use std::{
4 process::{Command, Output, Stdio},
5 thread,
6};
7
8pub fn has_sudo() -> bool {
9 Command::new("sudo")
10 .arg("-h")
11 .output()
12 .map(|output| output.status.success())
13 .unwrap_or(false)
14}
15
16pub fn is_root() -> bool {
17 unsafe { libc::getuid() == 0 }
18}
19
20pub fn run_command(command: &str, args: &[&str], use_sudo: bool) -> Result<Output> {
21 let mut cmd = if use_sudo {
22 if !has_sudo() && !is_root() {
23 return Err(anyhow!(
24 "sudo is required for command '{}', but not available",
25 command
26 ));
27 }
28 let mut c = Command::new("sudo");
29 c.arg(command);
30
31 match is_root() {
32 true => Command::new(command),
33 false => c,
34 }
35 } else {
36 Command::new(command)
37 };
38
39 let output = cmd
40 .args(args)
41 .stdin(Stdio::inherit())
42 .stderr(Stdio::piped())
43 .output()
44 .with_context(|| format!("Failed to execute {}", command))?;
45
46 if !output.status.success() {
47 let stderr = String::from_utf8_lossy(&output.stderr);
48 return Err(anyhow!("Command {} failed: {}", command, stderr));
49 }
50 Ok(output)
51}
52
53pub fn run_command_in_background(command: &str, args: &[&str], use_sudo: bool) -> Result<u32> {
54 let mut cmd = if use_sudo {
55 if !has_sudo() && !is_root() {
56 return Err(anyhow!(
57 "sudo is required for command '{}', but not available",
58 command
59 ));
60 }
61
62 let status = Command::new("sudo")
63 .arg("-v")
64 .stdin(Stdio::inherit())
65 .stdout(Stdio::inherit())
66 .stderr(Stdio::inherit())
67 .status()
68 .context("failed to run 'sudo -v' for credential validation")?;
69
70 if !status.success() {
71 return Err(anyhow!("'sudo -v' failed (wrong password or sudo policy)"));
72 }
73
74 let mut c = Command::new("sudo");
75 c.arg(command);
76
77 match is_root() {
78 true => Command::new(command),
79 false => c,
80 }
81 } else {
82 Command::new(command)
83 };
84
85 let command_owned = command.to_string();
86 let args_owned: Vec<String> = args.iter().map(|s| s.to_string()).collect();
87
88 let (tx, rx) = std::sync::mpsc::channel::<u32>();
89
90 thread::spawn(move || {
91 let mut child = cmd
92 .args(&args_owned)
93 .stdin(Stdio::null())
94 .stdout(Stdio::null())
95 .stderr(Stdio::null())
96 .spawn()
97 .with_context(|| format!("Failed to execute {}", command_owned))?;
98
99 let pid = child.id();
100 tx.send(pid)
101 .with_context(|| format!("Failed to send PID for command {}", command_owned))?;
102 println!(
103 "[+] Started command {} with PID {}",
104 command_owned.bright_cyan(),
105 pid.bright_cyan()
106 );
107
108 child
109 .wait()
110 .with_context(|| format!("Failed to wait for command {}", command_owned))?;
111 Ok::<(), Error>(())
112 });
113
114 let pid = rx
115 .recv()
116 .with_context(|| format!("Failed to receive PID for command {}", command))?;
117
118 Ok(pid)
119}