• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Provides `VhostUserBackend`, a fixture of a vhost-user backend process.
6 
7 use std::process;
8 use std::process::Command;
9 use std::process::Stdio;
10 use std::thread;
11 use std::time::Duration;
12 
13 use anyhow::Result;
14 use base::test_utils::check_can_sudo;
15 
16 use crate::utils::find_crosvm_binary;
17 
18 pub enum CmdType {
19     /// `crosvm device` command
20     Device,
21     /// `crosvm devices` command that is newer and supports sandboxing and multiple device
22     /// processes.
23     Devices,
24 }
25 
26 impl CmdType {
to_subcommand(&self) -> &str27     fn to_subcommand(&self) -> &str {
28         match self {
29             // `crosvm device`
30             CmdType::Device => "device",
31             // `crosvm devices`
32             CmdType::Devices => "devices",
33         }
34     }
35 }
36 
37 pub struct Config {
38     cmd_type: CmdType,
39     dev_name: String,
40     extra_args: Vec<String>,
41 }
42 
43 impl Config {
new(cmd_type: CmdType, name: &str) -> Self44     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     /// Uses extra arguments for `crosvm (device|devices)`.
extra_args(mut self, args: Vec<String>) -> Self53     pub fn extra_args(mut self, args: Vec<String>) -> Self {
54         self.extra_args = args;
55         self
56     }
57 }
58 
59 #[derive(Default)]
60 pub struct VhostUserBackend {
61     name: String,
62     process: Option<process::Child>,
63 }
64 
65 impl VhostUserBackend {
new(cfg: Config) -> Result<Self>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     /// Start up Vhost User Backend `sudo`.
new_sudo(cfg: Config) -> Result<Self>72     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 
new_common(mut cmd: Command, cfg: Config) -> Result<Self>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         // TODO(b/269174700): Wait for the VU socket to be available instead.
91         thread::sleep(Duration::from_millis(100));
92 
93         Ok(Self {
94             name: cfg.dev_name,
95             process,
96         })
97     }
98 }
99 
100 impl Drop for VhostUserBackend {
drop(&mut self)101     fn drop(&mut self) {
102         let output = self.process.take().unwrap().wait_with_output().unwrap();
103 
104         // Print both the crosvm's stdout/stderr to stdout so that they'll be shown when the test
105         // is failed.
106         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 }
125