1use std::process;
8use std::process::Command;
9use std::process::Stdio;
10use std::thread;
11use std::time::Duration;
12
13use anyhow::Result;
14use base::test_utils::check_can_sudo;
15
16use crate::utils::find_crosvm_binary;
17
18pub enum CmdType {
19 Device,
21 Devices,
24}
25
26impl CmdType {
27 fn to_subcommand(&self) -> &str {
28 match self {
29 CmdType::Device => "device",
31 CmdType::Devices => "devices",
33 }
34 }
35}
36
37pub struct Config {
38 cmd_type: CmdType,
39 dev_name: String,
40 extra_args: Vec<String>,
41}
42
43impl Config {
44 pub fn new(cmd_type: CmdType, name: &str) -> Self {
45 Config {
46 cmd_type,
47 dev_name: name.to_string(),
48 extra_args: Default::default(),
49 }
50 }
51
52 pub fn extra_args(mut self, args: Vec<String>) -> Self {
54 self.extra_args = args;
55 self
56 }
57}
58
59#[derive(Default)]
60pub struct VhostUserBackend {
61 name: String,
62 process: Option<process::Child>,
63}
64
65impl VhostUserBackend {
66 pub fn new(cfg: Config) -> Result<Self> {
67 let cmd = Command::new(find_crosvm_binary());
68 Self::new_common(cmd, cfg)
69 }
70
71 pub fn new_sudo(cfg: Config) -> Result<Self> {
73 check_can_sudo();
74
75 let mut cmd = Command::new("sudo");
76 cmd.arg(find_crosvm_binary());
77 Self::new_common(cmd, cfg)
78 }
79
80 fn new_common(mut cmd: Command, cfg: Config) -> Result<Self> {
81 cmd.args([cfg.cmd_type.to_subcommand()]);
82 cmd.args(cfg.extra_args);
83
84 cmd.stdout(Stdio::piped());
85 cmd.stderr(Stdio::piped());
86
87 println!("$ {cmd:?}");
88
89 let process = Some(cmd.spawn()?);
90 thread::sleep(Duration::from_millis(100));
92
93 Ok(Self {
94 name: cfg.dev_name,
95 process,
96 })
97 }
98}
99
100impl Drop for VhostUserBackend {
101 fn drop(&mut self) {
102 let output = self.process.take().unwrap().wait_with_output().unwrap();
103
104 println!(
107 "VhostUserBackend {} stdout:\n{}",
108 self.name,
109 std::str::from_utf8(&output.stdout).unwrap()
110 );
111 println!(
112 "VhostUserBackend {} stderr:\n{}",
113 self.name,
114 std::str::from_utf8(&output.stderr).unwrap()
115 );
116
117 if !output.status.success() {
118 panic!(
119 "VhostUserBackend {} exited illegally: {}",
120 self.name, output.status
121 );
122 }
123 }
124}