blob: 78dfbf29091fb0dd760a120dae5ef88ab0a131b7 [file] [log] [blame]
Zach Reizner639d9672017-05-01 17:57:18 -07001// Copyright 2017 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Runs a virtual machine under KVM
6
Zach Reiznerefe95782017-08-26 18:05:48 -07007pub mod argument;
Zach Reizner39aa26b2017-12-12 18:03:23 -08008pub mod linux;
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08009pub mod panic_hook;
Zach Reizner8864cb02018-01-16 17:59:03 -080010#[cfg(feature = "plugin")]
11pub mod plugin;
Zach Reizner29ad3c72017-08-04 15:12:58 -070012
Trent Begin17ccaad2019-04-17 13:51:25 -060013use std::collections::BTreeMap;
Jingkui Wang100e6e42019-03-08 20:41:57 -080014use std::fmt;
15use std::fs::{File, OpenOptions};
Stephen Barber2cfc2052017-06-21 15:16:11 -070016use std::net;
Jingkui Wang100e6e42019-03-08 20:41:57 -080017use std::num::ParseIntError;
18use std::os::unix::io::{FromRawFd, RawFd};
19use std::path::{Path, PathBuf};
Zach Reizner639d9672017-05-01 17:57:18 -070020use std::string::String;
Zach Reizner39aa26b2017-12-12 18:03:23 -080021use std::thread::sleep;
Stephen Barber56fbf092017-06-29 16:12:14 -070022use std::time::Duration;
Zach Reizner639d9672017-05-01 17:57:18 -070023
Trent Begin17ccaad2019-04-17 13:51:25 -060024use devices::{SerialParameters, SerialType};
David Tolnayfe3ef7d2019-03-08 15:57:49 -080025use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
Dylan Reid2dcb6322018-07-13 10:42:48 -070026use qcow::QcowFile;
Jingkui Wang100e6e42019-03-08 20:41:57 -080027use sys_util::{
David Tolnay633426a2019-04-12 12:18:35 -070028 debug, error, getpid, info, kill_process_group, net::UnixSeqpacket, reap_child, syslog,
29 validate_raw_fd, warn,
Jingkui Wang100e6e42019-03-08 20:41:57 -080030};
Jakub Starone7c59052019-04-09 12:31:14 -070031use vm_control::{
Jakub Staron1f828d72019-04-11 12:49:29 -070032 BalloonControlCommand, DiskControlCommand, MaybeOwnedFd, UsbControlCommand, UsbControlResult,
Zach Reizneraff94ca2019-03-18 20:58:31 -070033 VmControlRequestSocket, VmRequest, VmResponse, USB_CONTROL_MAX_PORTS,
Jakub Starone7c59052019-04-09 12:31:14 -070034};
Zach Reizner639d9672017-05-01 17:57:18 -070035
David Tolnayfe3ef7d2019-03-08 15:57:49 -080036use crate::argument::{print_help, set_arguments, Argument};
37
Zach Reizner39aa26b2017-12-12 18:03:23 -080038static SECCOMP_POLICY_DIR: &'static str = "/usr/share/policy/crosvm";
Zach Reizneree73bf32017-08-29 16:33:28 -070039
Zach Reiznerefe95782017-08-26 18:05:48 -070040struct DiskOption {
41 path: PathBuf,
Daniel Verkampde9ae032018-08-09 16:26:59 -070042 read_only: bool,
Dylan Reidf463bc12017-07-12 19:24:57 -070043}
44
Daniel Verkampbd1a0842019-01-08 15:50:34 -080045#[allow(dead_code)]
Chirantan Ekboted41d7262018-11-16 16:37:45 -080046struct BindMount {
47 src: PathBuf,
48 dst: PathBuf,
49 writable: bool,
50}
51
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -080052#[allow(dead_code)]
53struct GidMap {
54 inner: libc::gid_t,
55 outer: libc::gid_t,
56 count: u32,
57}
58
Jorge E. Moreira99d3f082019-03-07 10:59:54 -080059const DEFAULT_TOUCH_DEVICE_WIDTH: u32 = 800;
60const DEFAULT_TOUCH_DEVICE_HEIGHT: u32 = 1280;
Jorge E. Moreiradffec502019-01-14 18:44:49 -080061
Jorge E. Moreira99d3f082019-03-07 10:59:54 -080062struct TouchDeviceOption {
Jorge E. Moreiradffec502019-01-14 18:44:49 -080063 path: PathBuf,
64 width: u32,
65 height: u32,
66}
67
Jorge E. Moreira99d3f082019-03-07 10:59:54 -080068impl TouchDeviceOption {
69 fn new(path: PathBuf) -> TouchDeviceOption {
70 TouchDeviceOption {
Jorge E. Moreiradffec502019-01-14 18:44:49 -080071 path,
Jorge E. Moreira99d3f082019-03-07 10:59:54 -080072 width: DEFAULT_TOUCH_DEVICE_WIDTH,
73 height: DEFAULT_TOUCH_DEVICE_HEIGHT,
Jorge E. Moreiradffec502019-01-14 18:44:49 -080074 }
75 }
76}
77
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070078#[derive(Debug)]
79pub enum Executable {
80 Bios(PathBuf),
81 Kernel(PathBuf),
82 Plugin(PathBuf),
83}
84
85fn executable_is_plugin(executable: &Option<Executable>) -> bool {
86 match executable {
87 Some(Executable::Plugin(_)) => true,
88 _ => false,
89 }
90}
91
Daniel Verkampaac28132018-10-15 14:58:48 -070092pub struct Config {
93 vcpu_count: Option<u32>,
Daniel Verkamp107edb32019-04-05 09:58:48 -070094 vcpu_affinity: Vec<usize>,
Daniel Verkampaac28132018-10-15 14:58:48 -070095 memory: Option<usize>,
Cody Schuffelen6d1ab502019-05-21 12:12:38 -070096 executable_path: Option<Executable>,
Tristan Muntsinger4133b012018-12-21 16:01:56 -080097 android_fstab: Option<PathBuf>,
Daniel Verkampe403f5c2018-12-11 16:29:26 -080098 initrd_path: Option<PathBuf>,
Daniel Verkampaac28132018-10-15 14:58:48 -070099 params: Vec<String>,
100 socket_path: Option<PathBuf>,
Daniel Verkampaac28132018-10-15 14:58:48 -0700101 plugin_root: Option<PathBuf>,
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800102 plugin_mounts: Vec<BindMount>,
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -0800103 plugin_gid_maps: Vec<GidMap>,
Zach Reiznerefe95782017-08-26 18:05:48 -0700104 disks: Vec<DiskOption>,
Jakub Starona3411ea2019-04-24 10:55:25 -0700105 pmem_devices: Vec<DiskOption>,
Stephen Barber2cfc2052017-06-21 15:16:11 -0700106 host_ip: Option<net::Ipv4Addr>,
107 netmask: Option<net::Ipv4Addr>,
Stephen Barber308ff602018-02-13 22:47:07 -0800108 mac_address: Option<net_util::MacAddress>,
Stephen Barber5e77e882017-08-07 17:13:38 -0700109 vhost_net: bool,
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800110 tap_fd: Vec<RawFd>,
Dylan Reid059a1882018-07-23 17:58:09 -0700111 cid: Option<u64>,
Stephen Barber28a5a612017-10-20 17:15:30 -0700112 wayland_socket_path: Option<PathBuf>,
David Reveman52ba4e52018-04-22 21:42:09 -0400113 wayland_dmabuf: bool,
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700114 shared_dirs: Vec<(PathBuf, String)>,
Lepton Wu9105e9f2019-03-14 11:38:31 -0700115 sandbox: bool,
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700116 seccomp_policy_dir: PathBuf,
Zach Reizner44863792019-06-26 14:22:08 -0700117 seccomp_log_failures: bool,
Zach Reizner3a8100a2017-09-13 19:15:43 -0700118 gpu: bool,
David Tolnay43f8e212019-02-13 17:28:16 -0800119 software_tpm: bool,
paulhsiaf052cfe2019-01-22 15:22:25 +0800120 cras_audio: bool,
paulhsia580d4182019-05-24 16:53:55 +0800121 cras_capture: bool,
Dylan Reid3082e8e2019-01-07 10:33:48 -0800122 null_audio: bool,
Trent Begin17ccaad2019-04-17 13:51:25 -0600123 serial_parameters: BTreeMap<u8, SerialParameters>,
124 syslog_tag: Option<String>,
Jorge E. Moreira99d3f082019-03-07 10:59:54 -0800125 virtio_single_touch: Option<TouchDeviceOption>,
126 virtio_trackpad: Option<TouchDeviceOption>,
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800127 virtio_mouse: Option<PathBuf>,
128 virtio_keyboard: Option<PathBuf>,
129 virtio_input_evdevs: Vec<PathBuf>,
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800130 split_irqchip: bool,
Dylan Reid059a1882018-07-23 17:58:09 -0700131}
132
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700133impl Default for Config {
134 fn default() -> Config {
135 Config {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700136 vcpu_count: None,
Daniel Verkamp107edb32019-04-05 09:58:48 -0700137 vcpu_affinity: Vec::new(),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700138 memory: None,
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700139 executable_path: None,
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800140 android_fstab: None,
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800141 initrd_path: None,
Zach Reiznerbb678712018-01-30 18:13:04 -0800142 params: Vec::new(),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700143 socket_path: None,
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700144 plugin_root: None,
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800145 plugin_mounts: Vec::new(),
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -0800146 plugin_gid_maps: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700147 disks: Vec::new(),
Jakub Starona3411ea2019-04-24 10:55:25 -0700148 pmem_devices: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700149 host_ip: None,
150 netmask: None,
151 mac_address: None,
152 vhost_net: false,
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800153 tap_fd: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700154 cid: None,
155 gpu: false,
David Tolnay43f8e212019-02-13 17:28:16 -0800156 software_tpm: false,
Daniel Verkampaac28132018-10-15 14:58:48 -0700157 wayland_socket_path: None,
158 wayland_dmabuf: false,
159 shared_dirs: Vec::new(),
Lepton Wu9105e9f2019-03-14 11:38:31 -0700160 sandbox: !cfg!(feature = "default-no-sandbox"),
Daniel Verkampaac28132018-10-15 14:58:48 -0700161 seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
Zach Reizner44863792019-06-26 14:22:08 -0700162 seccomp_log_failures: false,
paulhsiaf052cfe2019-01-22 15:22:25 +0800163 cras_audio: false,
paulhsia580d4182019-05-24 16:53:55 +0800164 cras_capture: false,
Dylan Reid3082e8e2019-01-07 10:33:48 -0800165 null_audio: false,
Trent Begin17ccaad2019-04-17 13:51:25 -0600166 serial_parameters: BTreeMap::new(),
167 syslog_tag: None,
Jorge E. Moreira99d3f082019-03-07 10:59:54 -0800168 virtio_single_touch: None,
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800169 virtio_trackpad: None,
170 virtio_mouse: None,
171 virtio_keyboard: None,
172 virtio_input_evdevs: Vec::new(),
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800173 split_irqchip: false,
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700174 }
175 }
176}
177
Stephen Barbera00753b2017-07-18 13:57:26 -0700178// Wait for all children to exit. Return true if they have all exited, false
179// otherwise.
180fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -0700181 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -0700182 const CHILD_WAIT_MS: u64 = 10;
183 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -0700184 loop {
Zach Reizner56158c82017-08-24 13:50:14 -0700185 match reap_child() {
186 Ok(0) => break,
187 // We expect ECHILD which indicates that there were no children left.
188 Err(e) if e.errno() == libc::ECHILD => return true,
189 Err(e) => {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800190 warn!("error while waiting for children: {}", e);
Zach Reizner56158c82017-08-24 13:50:14 -0700191 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -0700192 }
Zach Reizner56158c82017-08-24 13:50:14 -0700193 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -0700194 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -0700195 }
196 }
Zach Reizner56158c82017-08-24 13:50:14 -0700197 // There's no timeout option for waitpid which reap_child calls internally, so our only
198 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -0700199 sleep(Duration::from_millis(CHILD_WAIT_MS));
200 }
201
202 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -0800203 false
Stephen Barbera00753b2017-07-18 13:57:26 -0700204}
205
Daniel Verkamp107edb32019-04-05 09:58:48 -0700206/// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
207fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
208 let mut cpuset = Vec::new();
209 for part in s.split(',') {
210 let range: Vec<&str> = part.split('-').collect();
211 if range.len() == 0 || range.len() > 2 {
212 return Err(argument::Error::InvalidValue {
213 value: part.to_owned(),
214 expected: "invalid list syntax",
215 });
216 }
217 let first_cpu: usize = range[0]
218 .parse()
219 .map_err(|_| argument::Error::InvalidValue {
220 value: part.to_owned(),
221 expected: "CPU index must be a non-negative integer",
222 })?;
223 let last_cpu: usize = if range.len() == 2 {
224 range[1]
225 .parse()
226 .map_err(|_| argument::Error::InvalidValue {
227 value: part.to_owned(),
228 expected: "CPU index must be a non-negative integer",
229 })?
230 } else {
231 first_cpu
232 };
233
234 if last_cpu < first_cpu {
235 return Err(argument::Error::InvalidValue {
236 value: part.to_owned(),
237 expected: "CPU ranges must be from low to high",
238 });
239 }
240
241 for cpu in first_cpu..=last_cpu {
242 cpuset.push(cpu);
243 }
244 }
245 Ok(cpuset)
246}
247
Trent Begin17ccaad2019-04-17 13:51:25 -0600248fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
249 let mut serial_setting = SerialParameters {
250 type_: SerialType::Sink,
251 path: None,
Trent Begin923bab02019-06-17 13:48:06 -0600252 num: 1,
Trent Begin17ccaad2019-04-17 13:51:25 -0600253 console: false,
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700254 stdin: false,
Trent Begin17ccaad2019-04-17 13:51:25 -0600255 };
256
257 let opts = s
258 .split(",")
259 .map(|frag| frag.split("="))
260 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
261
262 for (k, v) in opts {
263 match k {
264 "type" => {
265 serial_setting.type_ = v
266 .parse::<SerialType>()
267 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
268 }
269 "num" => {
270 let num = v.parse::<u8>().map_err(|e| {
271 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
272 })?;
273 if num < 1 || num > 4 {
274 return Err(argument::Error::InvalidValue {
275 value: num.to_string(),
276 expected: "Serial port num must be between 1 - 4",
277 });
278 }
279 serial_setting.num = num;
280 }
281 "console" => {
282 serial_setting.console = v.parse::<bool>().map_err(|e| {
283 argument::Error::Syntax(format!(
284 "serial device console is not parseable: {}",
285 e
286 ))
287 })?
288 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700289 "stdin" => {
290 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
291 argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
292 })?
293 }
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700294 "path" => serial_setting.path = Some(PathBuf::from(v)),
Trent Begin17ccaad2019-04-17 13:51:25 -0600295 _ => {
296 return Err(argument::Error::UnknownArgument(format!(
297 "serial parameter {}",
298 k
299 )));
300 }
301 }
302 }
303
304 Ok(serial_setting)
305}
306
Zach Reiznerefe95782017-08-26 18:05:48 -0700307fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
308 match name {
309 "" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700310 if cfg.executable_path.is_some() {
311 return Err(argument::Error::TooManyArguments(format!(
312 "A VM executable was already specified: {:?}",
313 cfg.executable_path
314 )));
Zach Reiznerefe95782017-08-26 18:05:48 -0700315 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700316 let kernel_path = PathBuf::from(value.unwrap());
317 if !kernel_path.exists() {
318 return Err(argument::Error::InvalidValue {
319 value: value.unwrap().to_owned(),
320 expected: "this kernel path does not exist",
321 });
322 }
323 cfg.executable_path = Some(Executable::Kernel(kernel_path));
Zach Reiznerefe95782017-08-26 18:05:48 -0700324 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800325 "android-fstab" => {
326 if cfg.android_fstab.is_some()
327 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
328 {
329 return Err(argument::Error::TooManyArguments(
330 "expected exactly one android fstab path".to_owned(),
331 ));
332 } else {
333 let android_fstab = PathBuf::from(value.unwrap());
334 if !android_fstab.exists() {
335 return Err(argument::Error::InvalidValue {
336 value: value.unwrap().to_owned(),
337 expected: "this android fstab path does not exist",
338 });
339 }
340 cfg.android_fstab = Some(android_fstab);
341 }
342 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700343 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -0800344 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -0700345 }
346 "cpus" => {
347 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700348 return Err(argument::Error::TooManyArguments(
349 "`cpus` already given".to_owned(),
350 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700351 }
352 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -0700353 Some(
354 value
355 .unwrap()
356 .parse()
357 .map_err(|_| argument::Error::InvalidValue {
358 value: value.unwrap().to_owned(),
359 expected: "this value for `cpus` needs to be integer",
360 })?,
361 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700362 }
Daniel Verkamp107edb32019-04-05 09:58:48 -0700363 "cpu-affinity" => {
364 if cfg.vcpu_affinity.len() != 0 {
365 return Err(argument::Error::TooManyArguments(
366 "`cpu-affinity` already given".to_owned(),
367 ));
368 }
369 cfg.vcpu_affinity = parse_cpu_set(value.unwrap())?;
370 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700371 "mem" => {
372 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700373 return Err(argument::Error::TooManyArguments(
374 "`mem` already given".to_owned(),
375 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700376 }
377 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -0700378 Some(
379 value
380 .unwrap()
381 .parse()
382 .map_err(|_| argument::Error::InvalidValue {
383 value: value.unwrap().to_owned(),
384 expected: "this value for `mem` needs to be integer",
385 })?,
386 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700387 }
paulhsiaf052cfe2019-01-22 15:22:25 +0800388 "cras-audio" => {
389 cfg.cras_audio = true;
390 }
paulhsia580d4182019-05-24 16:53:55 +0800391 "cras-capture" => {
392 cfg.cras_capture = true;
393 }
Dylan Reid3082e8e2019-01-07 10:33:48 -0800394 "null-audio" => {
395 cfg.null_audio = true;
396 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600397 "serial" => {
398 let serial_params = parse_serial_options(value.unwrap())?;
399 let num = serial_params.num;
400 if cfg.serial_parameters.contains_key(&num) {
401 return Err(argument::Error::TooManyArguments(format!(
402 "serial num {}",
403 num
404 )));
405 }
406
407 if serial_params.console {
Jakub Staronb6515a92019-06-05 15:18:25 -0700408 for params in cfg.serial_parameters.values() {
Trent Begin17ccaad2019-04-17 13:51:25 -0600409 if params.console {
410 return Err(argument::Error::TooManyArguments(format!(
411 "serial device {} already set as console",
412 params.num
413 )));
414 }
415 }
416 }
417
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700418 if serial_params.stdin {
419 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
420 return Err(argument::Error::TooManyArguments(format!(
421 "serial device {} already connected to standard input",
422 previous_stdin.num
423 )));
424 }
425 }
426
Trent Begin17ccaad2019-04-17 13:51:25 -0600427 cfg.serial_parameters.insert(num, serial_params);
428 }
429 "syslog-tag" => {
430 if cfg.syslog_tag.is_some() {
431 return Err(argument::Error::TooManyArguments(
432 "`syslog-tag` already given".to_owned(),
433 ));
434 }
435 syslog::set_proc_name(value.unwrap());
436 cfg.syslog_tag = Some(value.unwrap().to_owned());
437 }
Daniel Verkamp6a8cd102019-06-26 15:17:46 -0700438 "root" | "rwroot" | "disk" | "rwdisk" | "qcow" | "rwqcow" => {
439 let read_only = !name.starts_with("rw");
Zach Reiznerefe95782017-08-26 18:05:48 -0700440 let disk_path = PathBuf::from(value.unwrap());
441 if !disk_path.exists() {
442 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700443 value: value.unwrap().to_owned(),
444 expected: "this disk path does not exist",
445 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700446 }
Daniel Verkamp6a8cd102019-06-26 15:17:46 -0700447 if name.ends_with("root") {
Daniel Verkampaac28132018-10-15 14:58:48 -0700448 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -0700449 return Err(argument::Error::TooManyArguments(
450 "ran out of letters for to assign to root disk".to_owned(),
451 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700452 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700453 cfg.params.push(format!(
Daniel Verkamp6a8cd102019-06-26 15:17:46 -0700454 "root=/dev/vd{} {}",
455 char::from(b'a' + cfg.disks.len() as u8),
456 if read_only { "ro" } else { "rw" }
Zach Reizner55a9e502018-10-03 10:22:32 -0700457 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700458 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700459 cfg.disks.push(DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -0700460 path: disk_path,
Daniel Verkamp6a8cd102019-06-26 15:17:46 -0700461 read_only,
Zach Reizner55a9e502018-10-03 10:22:32 -0700462 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700463 }
Jakub Starona3411ea2019-04-24 10:55:25 -0700464 "pmem-device" | "rw-pmem-device" => {
465 let disk_path = PathBuf::from(value.unwrap());
466 if !disk_path.exists() {
467 return Err(argument::Error::InvalidValue {
468 value: value.unwrap().to_owned(),
469 expected: "this disk path does not exist",
470 });
471 }
472
473 cfg.pmem_devices.push(DiskOption {
474 path: disk_path,
475 read_only: !name.starts_with("rw"),
476 });
477 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700478 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700479 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700480 return Err(argument::Error::TooManyArguments(
481 "`host_ip` already given".to_owned(),
482 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700483 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700484 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -0700485 Some(
486 value
487 .unwrap()
488 .parse()
489 .map_err(|_| argument::Error::InvalidValue {
490 value: value.unwrap().to_owned(),
491 expected: "`host_ip` needs to be in the form \"x.x.x.x\"",
492 })?,
493 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700494 }
495 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700496 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700497 return Err(argument::Error::TooManyArguments(
498 "`netmask` already given".to_owned(),
499 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700500 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700501 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -0700502 Some(
503 value
504 .unwrap()
505 .parse()
506 .map_err(|_| argument::Error::InvalidValue {
507 value: value.unwrap().to_owned(),
508 expected: "`netmask` needs to be in the form \"x.x.x.x\"",
509 })?,
510 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700511 }
512 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700513 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700514 return Err(argument::Error::TooManyArguments(
515 "`mac` already given".to_owned(),
516 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700517 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700518 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -0700519 Some(
520 value
521 .unwrap()
522 .parse()
523 .map_err(|_| argument::Error::InvalidValue {
524 value: value.unwrap().to_owned(),
525 expected: "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
526 })?,
527 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700528 }
Stephen Barber28a5a612017-10-20 17:15:30 -0700529 "wayland-sock" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700530 if cfg.wayland_socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700531 return Err(argument::Error::TooManyArguments(
532 "`wayland-sock` already given".to_owned(),
533 ));
Stephen Barber28a5a612017-10-20 17:15:30 -0700534 }
535 let wayland_socket_path = PathBuf::from(value.unwrap());
536 if !wayland_socket_path.exists() {
537 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700538 value: value.unwrap().to_string(),
539 expected: "Wayland socket does not exist",
540 });
Stephen Barber28a5a612017-10-20 17:15:30 -0700541 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700542 cfg.wayland_socket_path = Some(wayland_socket_path);
Stephen Barber28a5a612017-10-20 17:15:30 -0700543 }
David Reveman52ba4e52018-04-22 21:42:09 -0400544 #[cfg(feature = "wl-dmabuf")]
Daniel Verkampaac28132018-10-15 14:58:48 -0700545 "wayland-dmabuf" => cfg.wayland_dmabuf = true,
Zach Reiznerefe95782017-08-26 18:05:48 -0700546 "socket" => {
547 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700548 return Err(argument::Error::TooManyArguments(
549 "`socket` already given".to_owned(),
550 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700551 }
552 let mut socket_path = PathBuf::from(value.unwrap());
553 if socket_path.is_dir() {
554 socket_path.push(format!("crosvm-{}.sock", getpid()));
555 }
556 if socket_path.exists() {
557 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700558 value: socket_path.to_string_lossy().into_owned(),
559 expected: "this socket path already exists",
560 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700561 }
562 cfg.socket_path = Some(socket_path);
563 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700564 "disable-sandbox" => {
Lepton Wu9105e9f2019-03-14 11:38:31 -0700565 cfg.sandbox = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700566 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700567 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700568 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700569 return Err(argument::Error::TooManyArguments(
570 "`cid` alread given".to_owned(),
571 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700572 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700573 cfg.cid = Some(
574 value
575 .unwrap()
576 .parse()
577 .map_err(|_| argument::Error::InvalidValue {
578 value: value.unwrap().to_owned(),
579 expected: "this value for `cid` must be an unsigned integer",
580 })?,
581 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700582 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700583 "shared-dir" => {
584 // Formatted as <src:tag>.
585 let param = value.unwrap();
586 let mut components = param.splitn(2, ':');
Zach Reizner55a9e502018-10-03 10:22:32 -0700587 let src =
588 PathBuf::from(
589 components
590 .next()
591 .ok_or_else(|| argument::Error::InvalidValue {
592 value: param.to_owned(),
593 expected: "missing source path for `shared-dir`",
594 })?,
595 );
596 let tag = components
597 .next()
598 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700599 value: param.to_owned(),
600 expected: "missing tag for `shared-dir`",
David Tolnay2bac1e72018-12-12 14:33:42 -0800601 })?
602 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700603
604 if !src.is_dir() {
605 return Err(argument::Error::InvalidValue {
606 value: param.to_owned(),
607 expected: "source path for `shared-dir` must be a directory",
608 });
609 }
610
Daniel Verkampaac28132018-10-15 14:58:48 -0700611 cfg.shared_dirs.push((src, tag));
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700612 }
Dylan Reide026ef02017-10-02 19:03:52 -0700613 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700614 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -0700615 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -0700616 }
Zach Reizner44863792019-06-26 14:22:08 -0700617 "seccomp-log-failures" => {
618 cfg.seccomp_log_failures = true;
619 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800620 "plugin" => {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700621 if cfg.executable_path.is_some() {
622 return Err(argument::Error::TooManyArguments(format!(
623 "A VM executable was already specified: {:?}",
624 cfg.executable_path
625 )));
Zach Reizner8864cb02018-01-16 17:59:03 -0800626 }
Zach Reiznercc30d582018-01-23 21:16:42 -0800627 let plugin = PathBuf::from(value.unwrap().to_owned());
628 if plugin.is_relative() {
629 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700630 value: plugin.to_string_lossy().into_owned(),
631 expected: "the plugin path must be an absolute path",
632 });
Zach Reiznercc30d582018-01-23 21:16:42 -0800633 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700634 cfg.executable_path = Some(Executable::Plugin(plugin));
Zach Reizner55a9e502018-10-03 10:22:32 -0700635 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700636 "plugin-root" => {
637 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -0700638 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800639 "plugin-mount" => {
640 let components: Vec<&str> = value.unwrap().split(":").collect();
641 if components.len() != 3 {
642 return Err(argument::Error::InvalidValue {
643 value: value.unwrap().to_owned(),
644 expected:
645 "`plugin-mount` must have exactly 3 components: <src>:<dst>:<writable>",
646 });
647 }
648
649 let src = PathBuf::from(components[0]);
650 if src.is_relative() {
651 return Err(argument::Error::InvalidValue {
652 value: components[0].to_owned(),
653 expected: "the source path for `plugin-mount` must be absolute",
654 });
655 }
656 if !src.exists() {
657 return Err(argument::Error::InvalidValue {
658 value: components[0].to_owned(),
659 expected: "the source path for `plugin-mount` does not exist",
660 });
661 }
662
663 let dst = PathBuf::from(components[1]);
664 if dst.is_relative() {
665 return Err(argument::Error::InvalidValue {
666 value: components[1].to_owned(),
667 expected: "the destination path for `plugin-mount` must be absolute",
668 });
669 }
670
671 let writable: bool =
672 components[2]
673 .parse()
674 .map_err(|_| argument::Error::InvalidValue {
675 value: components[2].to_owned(),
676 expected: "the <writable> component for `plugin-mount` is not valid bool",
677 })?;
678
679 cfg.plugin_mounts.push(BindMount { src, dst, writable });
680 }
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -0800681 "plugin-gid-map" => {
682 let components: Vec<&str> = value.unwrap().split(":").collect();
683 if components.len() != 3 {
684 return Err(argument::Error::InvalidValue {
685 value: value.unwrap().to_owned(),
686 expected:
687 "`plugin-gid-map` must have exactly 3 components: <inner>:<outer>:<count>",
688 });
689 }
690
691 let inner: libc::gid_t =
692 components[0]
693 .parse()
694 .map_err(|_| argument::Error::InvalidValue {
695 value: components[0].to_owned(),
696 expected: "the <inner> component for `plugin-gid-map` is not valid gid",
697 })?;
698
699 let outer: libc::gid_t =
700 components[1]
701 .parse()
702 .map_err(|_| argument::Error::InvalidValue {
703 value: components[1].to_owned(),
704 expected: "the <outer> component for `plugin-gid-map` is not valid gid",
705 })?;
706
707 let count: u32 = components[2]
708 .parse()
709 .map_err(|_| argument::Error::InvalidValue {
710 value: components[2].to_owned(),
711 expected: "the <count> component for `plugin-gid-map` is not valid number",
712 })?;
713
714 cfg.plugin_gid_maps.push(GidMap {
715 inner,
716 outer,
717 count,
718 });
719 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700720 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700721 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800722 cfg.tap_fd.push(
723 value
724 .unwrap()
725 .parse()
726 .map_err(|_| argument::Error::InvalidValue {
727 value: value.unwrap().to_owned(),
728 expected: "this value for `tap-fd` must be an unsigned integer",
729 })?,
730 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700731 }
Zach Reizner3a8100a2017-09-13 19:15:43 -0700732 "gpu" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700733 cfg.gpu = true;
Zach Reizner3a8100a2017-09-13 19:15:43 -0700734 }
David Tolnay43f8e212019-02-13 17:28:16 -0800735 "software-tpm" => {
736 cfg.software_tpm = true;
737 }
Jorge E. Moreira99d3f082019-03-07 10:59:54 -0800738 "single-touch" => {
739 if cfg.virtio_single_touch.is_some() {
740 return Err(argument::Error::TooManyArguments(
741 "`single-touch` already given".to_owned(),
742 ));
743 }
744 let mut it = value.unwrap().split(":");
745
746 let mut single_touch_spec =
747 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
748 if let Some(width) = it.next() {
749 single_touch_spec.width = width.trim().parse().unwrap();
750 }
751 if let Some(height) = it.next() {
752 single_touch_spec.height = height.trim().parse().unwrap();
753 }
754
755 cfg.virtio_single_touch = Some(single_touch_spec);
756 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800757 "trackpad" => {
758 if cfg.virtio_trackpad.is_some() {
759 return Err(argument::Error::TooManyArguments(
760 "`trackpad` already given".to_owned(),
761 ));
762 }
763 let mut it = value.unwrap().split(":");
764
765 let mut trackpad_spec =
Jorge E. Moreira99d3f082019-03-07 10:59:54 -0800766 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800767 if let Some(width) = it.next() {
768 trackpad_spec.width = width.trim().parse().unwrap();
769 }
770 if let Some(height) = it.next() {
771 trackpad_spec.height = height.trim().parse().unwrap();
772 }
773
774 cfg.virtio_trackpad = Some(trackpad_spec);
775 }
776 "mouse" => {
777 if cfg.virtio_mouse.is_some() {
778 return Err(argument::Error::TooManyArguments(
779 "`mouse` already given".to_owned(),
780 ));
781 }
782 cfg.virtio_mouse = Some(PathBuf::from(value.unwrap().to_owned()));
783 }
784 "keyboard" => {
785 if cfg.virtio_keyboard.is_some() {
786 return Err(argument::Error::TooManyArguments(
787 "`keyboard` already given".to_owned(),
788 ));
789 }
790 cfg.virtio_keyboard = Some(PathBuf::from(value.unwrap().to_owned()));
791 }
792 "evdev" => {
793 let dev_path = PathBuf::from(value.unwrap());
794 if !dev_path.exists() {
795 return Err(argument::Error::InvalidValue {
796 value: value.unwrap().to_owned(),
797 expected: "this input device path does not exist",
798 });
799 }
800 cfg.virtio_input_evdevs.push(dev_path);
801 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800802 "split-irqchip" => {
803 cfg.split_irqchip = true;
804 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800805 "initrd" => {
806 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
807 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700808 "bios" => {
809 if cfg.executable_path.is_some() {
810 return Err(argument::Error::TooManyArguments(format!(
811 "A VM executable was already specified: {:?}",
812 cfg.executable_path
813 )));
814 }
815 cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
816 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700817 "help" => return Err(argument::Error::PrintHelp),
818 _ => unreachable!(),
819 }
820 Ok(())
821}
822
Dylan Reidbfba9932018-02-05 15:51:59 -0800823fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -0700824 let arguments =
825 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800826 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800827 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700828 Argument::short_value('p',
829 "params",
830 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -0800831 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700832 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700833 Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on. (e.g. 0,1-3,5) (default: no mask)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700834 Argument::short_value('m',
835 "mem",
836 "N",
837 "Amount of guest memory in MiB. (default: 256)"),
838 Argument::short_value('r',
839 "root",
840 "PATH",
841 "Path to a root disk image. Like `--disk` but adds appropriate kernel command line option."),
Daniel Verkamp6a8cd102019-06-26 15:17:46 -0700842 Argument::value("rwroot", "PATH", "Path to a writable root disk image."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700843 Argument::short_value('d', "disk", "PATH", "Path to a disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700844 Argument::value("qcow", "PATH", "Path to a qcow2 disk image. (Deprecated; use --disk instead.)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700845 Argument::value("rwdisk", "PATH", "Path to a writable disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700846 Argument::value("rwqcow", "PATH", "Path to a writable qcow2 disk image. (Deprecated; use --rwdisk instead.)"),
Jakub Starona3411ea2019-04-24 10:55:25 -0700847 Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
848 Argument::value("pmem-device", "PATH", "Path to a disk image."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700849 Argument::value("host_ip",
850 "IP",
851 "IP address to assign to host tap interface."),
852 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
853 Argument::value("mac", "MAC", "MAC address for VM."),
paulhsiaf052cfe2019-01-22 15:22:25 +0800854 Argument::flag("cras-audio", "Add an audio device to the VM that plays samples through CRAS server"),
paulhsia580d4182019-05-24 16:53:55 +0800855 Argument::flag("cras-capture", "Enable capturing audio from CRAS server to the cras-audio device"),
Dylan Reid3082e8e2019-01-07 10:33:48 -0800856 Argument::flag("null-audio", "Add an audio device to the VM that plays samples to /dev/null"),
Trent Begin17ccaad2019-04-17 13:51:25 -0600857 Argument::value("serial",
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700858 "type=TYPE,[num=NUM,path=PATH,console,stdin]",
Trent Begin17ccaad2019-04-17 13:51:25 -0600859 "Comma seperated key=value pairs for setting up serial devices. Can be given more than once.
860 Possible key values:
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700861 type=(stdout,syslog,sink,file) - Where to route the serial device
Trent Begin923bab02019-06-17 13:48:06 -0600862 num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700863 path=PATH - The path to the file to write to when type=file
Trent Begin17ccaad2019-04-17 13:51:25 -0600864 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
Jorge E. Moreira1e262302019-08-01 14:40:03 -0700865 stdin - Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided.
Trent Begin17ccaad2019-04-17 13:51:25 -0600866 "),
867 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
Stephen Barber28a5a612017-10-20 17:15:30 -0700868 Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
David Reveman52ba4e52018-04-22 21:42:09 -0400869 #[cfg(feature = "wl-dmabuf")]
870 Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700871 Argument::short_value('s',
872 "socket",
873 "PATH",
874 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700875 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700876 Argument::value("cid", "CID", "Context ID for virtual sockets."),
877 Argument::value("shared-dir", "PATH:TAG",
878 "Directory to be shared with a VM as a source:tag pair. Can be given more than once."),
Dylan Reide026ef02017-10-02 19:03:52 -0700879 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner44863792019-06-26 14:22:08 -0700880 Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),
Zach Reizner8864cb02018-01-16 17:59:03 -0800881 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -0800882 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -0800883 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700884 Argument::value("plugin-root", "PATH", "Absolute path to a directory that will become root filesystem for the plugin process."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -0800885 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800886 Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem. Can be given more than once."),
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -0800887 #[cfg(feature = "plugin")]
888 Argument::value("plugin-gid-map", "GID:GID:INT", "Supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
Rob Bradford8f002f52018-02-19 16:31:11 +0000889 Argument::flag("vhost-net", "Use vhost for networking."),
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700890 Argument::value("tap-fd",
891 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800892 "File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
Zach Reizner3a8100a2017-09-13 19:15:43 -0700893 #[cfg(feature = "gpu")]
894 Argument::flag("gpu", "(EXPERIMENTAL) enable virtio-gpu device"),
David Tolnay43f8e212019-02-13 17:28:16 -0800895 #[cfg(feature = "tpm")]
896 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800897 Argument::value("evdev", "PATH", "Path to an event device node. The device will be grabbed (unusable from the host) and made available to the guest with the same configuration it shows on the host"),
Jorge E. Moreira99d3f082019-03-07 10:59:54 -0800898 Argument::value("single-touch", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read single touch input events (such as those from a touchscreen) and write status updates to, optionally followed by width and height (defaults to 800x1280)."),
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800899 Argument::value("trackpad", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read trackpad input events and write status updates to, optionally followed by screen width and height (defaults to 800x1280)."),
900 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
901 Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800902 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
903 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700904 Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700905 Argument::short_flag('h', "help", "Print help message.")];
906
907 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -0700908 let match_res = set_arguments(args, &arguments[..], |name, value| {
909 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -0800910 })
911 .and_then(|_| {
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700912 if cfg.executable_path.is_none() {
Zach Reiznerefe95782017-08-26 18:05:48 -0700913 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
914 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700915 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
916 if cfg.host_ip.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700917 return Err(argument::Error::ExpectedArgument(
918 "`host_ip` missing from network config".to_owned(),
919 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700920 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700921 if cfg.netmask.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700922 return Err(argument::Error::ExpectedArgument(
923 "`netmask` missing from network config".to_owned(),
924 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700925 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700926 if cfg.mac_address.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700927 return Err(argument::Error::ExpectedArgument(
928 "`mac` missing from network config".to_owned(),
929 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700930 }
931 }
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700932 if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
Zach Reizner55a9e502018-10-03 10:22:32 -0700933 return Err(argument::Error::ExpectedArgument(
934 "`plugin-root` requires `plugin`".to_owned(),
935 ));
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700936 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700937 Ok(())
938 });
939
940 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -0800941 #[cfg(feature = "plugin")]
Cody Schuffelen6d1ab502019-05-21 12:12:38 -0700942 Ok(()) if executable_is_plugin(&cfg.executable_path) => match plugin::run_config(cfg) {
David Tolnay2bac1e72018-12-12 14:33:42 -0800943 Ok(_) => {
944 info!("crosvm and plugin have exited normally");
945 Ok(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800946 }
David Tolnay2bac1e72018-12-12 14:33:42 -0800947 Err(e) => {
948 error!("{}", e);
949 Err(())
950 }
951 },
Zach Reizner55a9e502018-10-03 10:22:32 -0700952 Ok(()) => match linux::run_config(cfg) {
953 Ok(_) => {
954 info!("crosvm has exited normally");
955 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -0700956 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700957 Err(e) => {
958 error!("{}", e);
959 Err(())
960 }
961 },
Dylan Reidbfba9932018-02-05 15:51:59 -0800962 Err(argument::Error::PrintHelp) => {
963 print_help("crosvm run", "KERNEL", &arguments[..]);
964 Ok(())
965 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800966 Err(e) => {
967 println!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -0800968 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800969 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700970 }
971}
972
Jingkui Wang100e6e42019-03-08 20:41:57 -0800973fn handle_request(
974 request: &VmRequest,
975 args: std::env::Args,
976) -> std::result::Result<VmResponse, ()> {
977 let mut return_result = Err(());
Zach Reiznerefe95782017-08-26 18:05:48 -0700978 for socket_path in args {
Zach Reiznera60744b2019-02-13 17:33:32 -0800979 match UnixSeqpacket::connect(&socket_path) {
Zach Reiznerefe95782017-08-26 18:05:48 -0700980 Ok(s) => {
Jakub Starone7c59052019-04-09 12:31:14 -0700981 let socket: VmControlRequestSocket = MsgSocket::new(s);
Zach Reizner78986322019-02-21 20:43:21 -0800982 if let Err(e) = socket.send(request) {
Zach Reizner55a9e502018-10-03 10:22:32 -0700983 error!(
David Tolnayb4bd00f2019-02-12 17:51:26 -0800984 "failed to send request to socket at '{}': {}",
Zach Reizner55a9e502018-10-03 10:22:32 -0700985 socket_path, e
986 );
Zach Reizner78986322019-02-21 20:43:21 -0800987 return_result = Err(());
988 continue;
Zach Reiznerefe95782017-08-26 18:05:48 -0700989 }
Zach Reizner78986322019-02-21 20:43:21 -0800990 match socket.recv() {
Jingkui Wang100e6e42019-03-08 20:41:57 -0800991 Ok(response) => return_result = Ok(response),
Zach Reizner78986322019-02-21 20:43:21 -0800992 Err(e) => {
993 error!(
994 "failed to send request to socket at2 '{}': {}",
995 socket_path, e
996 );
997 return_result = Err(());
998 continue;
999 }
Dylan Reidd4432042017-12-06 18:20:09 -08001000 }
1001 }
Dylan Reidbfba9932018-02-05 15:51:59 -08001002 Err(e) => {
1003 error!("failed to connect to socket at '{}': {}", socket_path, e);
1004 return_result = Err(());
1005 }
Dylan Reidd4432042017-12-06 18:20:09 -08001006 }
1007 }
Dylan Reidbfba9932018-02-05 15:51:59 -08001008
1009 return_result
Dylan Reidd4432042017-12-06 18:20:09 -08001010}
Zach Reiznerefe95782017-08-26 18:05:48 -07001011
Jingkui Wang100e6e42019-03-08 20:41:57 -08001012fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()> {
1013 let response = handle_request(request, args)?;
1014 info!("request response was {}", response);
1015 Ok(())
1016}
1017
Zach Reizner78986322019-02-21 20:43:21 -08001018fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> {
1019 if args.len() == 0 {
1020 print_help("crosvm stop", "VM_SOCKET...", &[]);
1021 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001022 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08001023 }
1024 vms_request(&VmRequest::Exit, args)
1025}
1026
1027fn suspend_vms(args: std::env::Args) -> std::result::Result<(), ()> {
1028 if args.len() == 0 {
1029 print_help("crosvm suspend", "VM_SOCKET...", &[]);
1030 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001031 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08001032 }
1033 vms_request(&VmRequest::Suspend, args)
1034}
1035
1036fn resume_vms(args: std::env::Args) -> std::result::Result<(), ()> {
1037 if args.len() == 0 {
1038 print_help("crosvm resume", "VM_SOCKET...", &[]);
1039 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001040 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08001041 }
1042 vms_request(&VmRequest::Resume, args)
1043}
1044
1045fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
1046 if args.len() < 2 {
1047 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
1048 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001049 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -08001050 }
1051 let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
1052 Ok(n) => n,
1053 Err(_) => {
1054 error!("Failed to parse number of bytes");
1055 return Err(());
1056 }
1057 };
1058
Jakub Staron1f828d72019-04-11 12:49:29 -07001059 let command = BalloonControlCommand::Adjust { num_bytes };
1060 vms_request(&VmRequest::BalloonCommand(command), args)
Zach Reizner78986322019-02-21 20:43:21 -08001061}
1062
Dylan Reid2dcb6322018-07-13 10:42:48 -07001063fn create_qcow2(mut args: std::env::Args) -> std::result::Result<(), ()> {
1064 if args.len() != 2 {
1065 print_help("crosvm create_qcow2", "PATH SIZE", &[]);
Dylan Reid940259c2018-07-20 14:22:33 -07001066 println!("Create a new QCOW2 image at `PATH` of the specified `SIZE` in bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001067 return Err(());
Dylan Reid2dcb6322018-07-13 10:42:48 -07001068 }
1069 let file_path = args.nth(0).unwrap();
1070 let size: u64 = match args.nth(0).unwrap().parse::<u64>() {
1071 Ok(n) => n,
1072 Err(_) => {
1073 error!("Failed to parse size of the disk.");
1074 return Err(());
Zach Reizner55a9e502018-10-03 10:22:32 -07001075 }
Dylan Reid2dcb6322018-07-13 10:42:48 -07001076 };
1077
1078 let file = OpenOptions::new()
1079 .create(true)
1080 .read(true)
1081 .write(true)
1082 .open(&file_path)
1083 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08001084 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -07001085 })?;
1086
Zach Reizner55a9e502018-10-03 10:22:32 -07001087 QcowFile::new(file, size).map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08001088 error!("Failed to create qcow file at '{}': {}", file_path, e);
Zach Reizner55a9e502018-10-03 10:22:32 -07001089 })?;
Dylan Reid2dcb6322018-07-13 10:42:48 -07001090
1091 Ok(())
1092}
1093
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001094fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
1095 if args.len() < 2 {
1096 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
1097 println!("Manage attached virtual disk devices.");
1098 println!("Subcommands:");
1099 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001100 return Err(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001101 }
1102 let subcommand: &str = &args.nth(0).unwrap();
1103
1104 let request = match subcommand {
1105 "resize" => {
1106 let disk_index = match args.nth(0).unwrap().parse::<usize>() {
1107 Ok(n) => n,
1108 Err(_) => {
1109 error!("Failed to parse disk index");
1110 return Err(());
1111 }
1112 };
1113
1114 let new_size = match args.nth(0).unwrap().parse::<u64>() {
1115 Ok(n) => n,
1116 Err(_) => {
1117 error!("Failed to parse disk size");
1118 return Err(());
1119 }
1120 };
1121
Jakub Staronecf81e02019-04-11 11:43:39 -07001122 VmRequest::DiskCommand {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001123 disk_index,
Jakub Staronecf81e02019-04-11 11:43:39 -07001124 command: DiskControlCommand::Resize { new_size },
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001125 }
1126 }
1127 _ => {
1128 error!("Unknown disk subcommand '{}'", subcommand);
1129 return Err(());
1130 }
1131 };
1132
Zach Reizner78986322019-02-21 20:43:21 -08001133 vms_request(&request, args)
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001134}
1135
Jingkui Wang100e6e42019-03-08 20:41:57 -08001136enum ModifyUsbError {
1137 ArgMissing(&'static str),
1138 ArgParse(&'static str, String),
1139 ArgParseInt(&'static str, String, ParseIntError),
1140 FailedFdValidate(sys_util::Error),
1141 PathDoesNotExist(PathBuf),
1142 SocketFailed,
1143 UnexpectedResponse(VmResponse),
1144 UnknownCommand(String),
1145 UsbControl(UsbControlResult),
1146}
1147
1148impl fmt::Display for ModifyUsbError {
1149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1150 use self::ModifyUsbError::*;
1151
1152 match self {
1153 ArgMissing(a) => write!(f, "argument missing: {}", a),
1154 ArgParse(name, value) => {
1155 write!(f, "failed to parse argument {} value `{}`", name, value)
1156 }
1157 ArgParseInt(name, value, e) => write!(
1158 f,
1159 "failed to parse integer argument {} value `{}`: {}",
1160 name, value, e
1161 ),
1162 FailedFdValidate(e) => write!(f, "failed to validate file descriptor: {}", e),
1163 PathDoesNotExist(p) => write!(f, "path `{}` does not exist", p.display()),
1164 SocketFailed => write!(f, "socket failed"),
1165 UnexpectedResponse(r) => write!(f, "unexpected response: {}", r),
1166 UnknownCommand(c) => write!(f, "unknown command: `{}`", c),
1167 UsbControl(e) => write!(f, "{}", e),
1168 }
1169 }
1170}
1171
1172type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
1173
1174fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
1175 debug!("parse_bus_id_addr: {}", v);
1176 let mut ids = v.split(":");
1177 match (ids.next(), ids.next(), ids.next(), ids.next()) {
1178 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
1179 let bus_id = bus_id
1180 .parse::<u8>()
1181 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
1182 let addr = addr
1183 .parse::<u8>()
1184 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
1185 let vid = u16::from_str_radix(&vid, 16)
1186 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
1187 let pid = u16::from_str_radix(&pid, 16)
1188 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
1189 Ok((bus_id, addr, vid, pid))
1190 }
1191 _ => Err(ModifyUsbError::ArgParse(
1192 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
1193 v.to_owned(),
1194 )),
1195 }
1196}
1197
1198fn raw_fd_from_path(path: &Path) -> ModifyUsbResult<RawFd> {
1199 if !path.exists() {
1200 return Err(ModifyUsbError::PathDoesNotExist(path.to_owned()));
1201 }
1202 let raw_fd = path
1203 .file_name()
1204 .and_then(|fd_osstr| fd_osstr.to_str())
1205 .map_or(
1206 Err(ModifyUsbError::ArgParse(
1207 "USB_DEVICE_PATH",
1208 path.to_string_lossy().into_owned(),
1209 )),
1210 |fd_str| {
1211 fd_str.parse::<libc::c_int>().map_err(|e| {
1212 ModifyUsbError::ArgParseInt("USB_DEVICE_PATH", fd_str.to_owned(), e)
1213 })
1214 },
1215 )?;
David Tolnay5fb3f512019-04-12 19:22:33 -07001216 validate_raw_fd(raw_fd).map_err(ModifyUsbError::FailedFdValidate)
Jingkui Wang100e6e42019-03-08 20:41:57 -08001217}
1218
1219fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
1220 let val = args
1221 .next()
1222 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
1223 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
1224 let dev_path = PathBuf::from(
1225 args.next()
1226 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
1227 );
1228 let usb_file: Option<File> = if dev_path == Path::new("-") {
1229 None
1230 } else if dev_path.parent() == Some(Path::new("/proc/self/fd")) {
1231 // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
1232 // Safe because we will validate |raw_fd|.
1233 Some(unsafe { File::from_raw_fd(raw_fd_from_path(&dev_path)?) })
1234 } else {
1235 Some(
1236 OpenOptions::new()
1237 .read(true)
1238 .write(true)
1239 .open(&dev_path)
1240 .map_err(|_| ModifyUsbError::UsbControl(UsbControlResult::FailedToOpenDevice))?,
1241 )
1242 };
1243
1244 let request = VmRequest::UsbCommand(UsbControlCommand::AttachDevice {
1245 bus,
1246 addr,
1247 vid,
1248 pid,
David Tolnay5fb3f512019-04-12 19:22:33 -07001249 fd: usb_file.map(MaybeOwnedFd::Owned),
Jingkui Wang100e6e42019-03-08 20:41:57 -08001250 });
1251 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
1252 match response {
1253 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
1254 r => Err(ModifyUsbError::UnexpectedResponse(r)),
1255 }
1256}
1257
1258fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
1259 let port: u8 = args
1260 .next()
1261 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
1262 p.parse::<u8>()
1263 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
1264 })?;
1265 let request = VmRequest::UsbCommand(UsbControlCommand::DetachDevice { port });
1266 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
1267 match response {
1268 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
1269 r => Err(ModifyUsbError::UnexpectedResponse(r)),
1270 }
1271}
1272
Zach Reizneraff94ca2019-03-18 20:58:31 -07001273fn usb_list(args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
1274 let mut ports: [u8; USB_CONTROL_MAX_PORTS] = Default::default();
1275 for (index, port) in ports.iter_mut().enumerate() {
1276 *port = index as u8
1277 }
1278 let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { ports });
Jingkui Wang100e6e42019-03-08 20:41:57 -08001279 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
1280 match response {
1281 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
1282 r => Err(ModifyUsbError::UnexpectedResponse(r)),
1283 }
1284}
1285
1286fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizneraff94ca2019-03-18 20:58:31 -07001287 if args.len() < 2 {
Jingkui Wang100e6e42019-03-08 20:41:57 -08001288 print_help("crosvm usb",
Zach Reizneraff94ca2019-03-18 20:58:31 -07001289 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
Jingkui Wang100e6e42019-03-08 20:41:57 -08001290 return Err(());
1291 }
1292
1293 // This unwrap will not panic because of the above length check.
1294 let command = args.next().unwrap();
1295 let result = match command.as_ref() {
1296 "attach" => usb_attach(args),
1297 "detach" => usb_detach(args),
1298 "list" => usb_list(args),
1299 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
1300 };
1301 match result {
1302 Ok(response) => {
1303 println!("{}", response);
1304 Ok(())
1305 }
1306 Err(e) => {
1307 println!("error {}", e);
1308 Err(())
1309 }
1310 }
1311}
1312
Zach Reiznerefe95782017-08-26 18:05:48 -07001313fn print_usage() {
1314 print_help("crosvm", "[stop|run]", &[]);
1315 println!("Commands:");
1316 println!(" stop - Stops crosvm instances via their control sockets.");
1317 println!(" run - Start a new crosvm instance.");
Dylan Reid2dcb6322018-07-13 10:42:48 -07001318 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08001319 println!(" disk - Manage attached virtual disk devices.");
1320 println!(" usb - Manage attached virtual USB devices.");
Zach Reiznerefe95782017-08-26 18:05:48 -07001321}
1322
Dylan Reidbfba9932018-02-05 15:51:59 -08001323fn crosvm_main() -> std::result::Result<(), ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -07001324 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -08001325 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08001326 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -07001327 }
Zach Reizner639d9672017-05-01 17:57:18 -07001328
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08001329 panic_hook::set_panic_hook();
1330
Zach Reiznerefe95782017-08-26 18:05:48 -07001331 let mut args = std::env::args();
1332 if args.next().is_none() {
1333 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -08001334 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -07001335 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001336
Zach Reizner8864cb02018-01-16 17:59:03 -08001337 // Past this point, usage of exit is in danger of leaking zombie processes.
Dylan Reidbfba9932018-02-05 15:51:59 -08001338 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
1339 None => {
1340 print_usage();
1341 Ok(())
1342 }
Zach Reizner55a9e502018-10-03 10:22:32 -07001343 Some("stop") => stop_vms(args),
Zach Reizner6a8fdd92019-01-16 14:38:41 -08001344 Some("suspend") => suspend_vms(args),
1345 Some("resume") => resume_vms(args),
Zach Reizner55a9e502018-10-03 10:22:32 -07001346 Some("run") => run_vm(args),
1347 Some("balloon") => balloon_vms(args),
1348 Some("create_qcow2") => create_qcow2(args),
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001349 Some("disk") => disk_cmd(args),
Jingkui Wang100e6e42019-03-08 20:41:57 -08001350 Some("usb") => modify_usb(args),
Zach Reiznerefe95782017-08-26 18:05:48 -07001351 Some(c) => {
1352 println!("invalid subcommand: {:?}", c);
1353 print_usage();
Dylan Reidbfba9932018-02-05 15:51:59 -08001354 Err(())
Zach Reiznerefe95782017-08-26 18:05:48 -07001355 }
Dylan Reidbfba9932018-02-05 15:51:59 -08001356 };
Zach Reiznerefe95782017-08-26 18:05:48 -07001357
1358 // Reap exit status from any child device processes. At this point, all devices should have been
1359 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
1360 // take some time for the processes to shut down.
1361 if !wait_all_children() {
1362 // We gave them a chance, and it's too late.
1363 warn!("not all child processes have exited; sending SIGKILL");
1364 if let Err(e) = kill_process_group() {
1365 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -08001366 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -07001367 }
1368 }
1369
1370 // WARNING: Any code added after this point is not guaranteed to run
1371 // since we may forcibly kill this process (and its children) above.
Dylan Reidbfba9932018-02-05 15:51:59 -08001372 ret
1373}
1374
1375fn main() {
1376 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
Zach Reizner639d9672017-05-01 17:57:18 -07001377}
Daniel Verkamp107edb32019-04-05 09:58:48 -07001378
1379#[cfg(test)]
1380mod tests {
1381 use super::*;
1382
1383 #[test]
1384 fn parse_cpu_set_single() {
1385 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
1386 }
1387
1388 #[test]
1389 fn parse_cpu_set_list() {
1390 assert_eq!(
1391 parse_cpu_set("0,1,2,3").expect("parse failed"),
1392 vec![0, 1, 2, 3]
1393 );
1394 }
1395
1396 #[test]
1397 fn parse_cpu_set_range() {
1398 assert_eq!(
1399 parse_cpu_set("0-3").expect("parse failed"),
1400 vec![0, 1, 2, 3]
1401 );
1402 }
1403
1404 #[test]
1405 fn parse_cpu_set_list_of_ranges() {
1406 assert_eq!(
1407 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
1408 vec![3, 4, 7, 8, 9, 18]
1409 );
1410 }
1411
1412 #[test]
1413 fn parse_cpu_set_repeated() {
1414 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
1415 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
1416 }
1417
1418 #[test]
1419 fn parse_cpu_set_negative() {
1420 // Negative CPU numbers are not allowed.
1421 parse_cpu_set("-3").expect_err("parse should have failed");
1422 }
1423
1424 #[test]
1425 fn parse_cpu_set_reverse_range() {
1426 // Ranges must be from low to high.
1427 parse_cpu_set("5-2").expect_err("parse should have failed");
1428 }
1429
1430 #[test]
1431 fn parse_cpu_set_open_range() {
1432 parse_cpu_set("3-").expect_err("parse should have failed");
1433 }
1434
1435 #[test]
1436 fn parse_cpu_set_extra_comma() {
1437 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
1438 }
Trent Begin17ccaad2019-04-17 13:51:25 -06001439
1440 #[test]
1441 fn parse_serial_vaild() {
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001442 parse_serial_options("type=syslog,num=1,console=true,stdin=true")
1443 .expect("parse should have succeded");
Trent Begin17ccaad2019-04-17 13:51:25 -06001444 }
1445
1446 #[test]
Trent Begin923bab02019-06-17 13:48:06 -06001447 fn parse_serial_valid_no_num() {
1448 parse_serial_options("type=syslog").expect("parse should have succeded");
1449 }
1450
1451 #[test]
Trent Begin17ccaad2019-04-17 13:51:25 -06001452 fn parse_serial_invalid_type() {
1453 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
1454 }
1455
1456 #[test]
1457 fn parse_serial_invalid_num_upper() {
1458 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
1459 }
1460
1461 #[test]
1462 fn parse_serial_invalid_num_lower() {
1463 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
1464 }
1465
1466 #[test]
1467 fn parse_serial_invalid_num_string() {
1468 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
1469 }
1470
1471 #[test]
1472 fn parse_serial_invalid_option() {
1473 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
1474 }
Jorge E. Moreira1e262302019-08-01 14:40:03 -07001475
1476 #[test]
1477 fn parse_serial_invalid_two_stdin() {
1478 let mut config = Config::default();
1479 set_argument(&mut config, "serial", Some("num=1,type=stdout,stdin=true"))
1480 .expect("should parse the first serial argument");
1481 set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
1482 .expect_err("should fail to parse a second serial port connected to stdin");
1483 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07001484}