blob: 092664ac8db07c01ef0d2b18ffa09a1e0f3da62c [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
Daniel Verkampaac28132018-10-15 14:58:48 -070078pub struct Config {
79 vcpu_count: Option<u32>,
Daniel Verkamp107edb32019-04-05 09:58:48 -070080 vcpu_affinity: Vec<usize>,
Daniel Verkampaac28132018-10-15 14:58:48 -070081 memory: Option<usize>,
82 kernel_path: PathBuf,
Tristan Muntsinger4133b012018-12-21 16:01:56 -080083 android_fstab: Option<PathBuf>,
Daniel Verkampe403f5c2018-12-11 16:29:26 -080084 initrd_path: Option<PathBuf>,
Daniel Verkampaac28132018-10-15 14:58:48 -070085 params: Vec<String>,
86 socket_path: Option<PathBuf>,
87 plugin: Option<PathBuf>,
88 plugin_root: Option<PathBuf>,
Chirantan Ekboted41d7262018-11-16 16:37:45 -080089 plugin_mounts: Vec<BindMount>,
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -080090 plugin_gid_maps: Vec<GidMap>,
Zach Reiznerefe95782017-08-26 18:05:48 -070091 disks: Vec<DiskOption>,
Stephen Barber2cfc2052017-06-21 15:16:11 -070092 host_ip: Option<net::Ipv4Addr>,
93 netmask: Option<net::Ipv4Addr>,
Stephen Barber308ff602018-02-13 22:47:07 -080094 mac_address: Option<net_util::MacAddress>,
Stephen Barber5e77e882017-08-07 17:13:38 -070095 vhost_net: bool,
Jorge E. Moreirab7952802019-02-12 16:43:05 -080096 tap_fd: Vec<RawFd>,
Dylan Reid059a1882018-07-23 17:58:09 -070097 cid: Option<u64>,
Stephen Barber28a5a612017-10-20 17:15:30 -070098 wayland_socket_path: Option<PathBuf>,
David Reveman52ba4e52018-04-22 21:42:09 -040099 wayland_dmabuf: bool,
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700100 shared_dirs: Vec<(PathBuf, String)>,
Lepton Wu9105e9f2019-03-14 11:38:31 -0700101 sandbox: bool,
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700102 seccomp_policy_dir: PathBuf,
Zach Reizner3a8100a2017-09-13 19:15:43 -0700103 gpu: bool,
David Tolnay43f8e212019-02-13 17:28:16 -0800104 software_tpm: bool,
paulhsiaf052cfe2019-01-22 15:22:25 +0800105 cras_audio: bool,
Dylan Reid3082e8e2019-01-07 10:33:48 -0800106 null_audio: bool,
Trent Begin17ccaad2019-04-17 13:51:25 -0600107 serial_parameters: BTreeMap<u8, SerialParameters>,
108 syslog_tag: Option<String>,
Jorge E. Moreira99d3f082019-03-07 10:59:54 -0800109 virtio_single_touch: Option<TouchDeviceOption>,
110 virtio_trackpad: Option<TouchDeviceOption>,
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800111 virtio_mouse: Option<PathBuf>,
112 virtio_keyboard: Option<PathBuf>,
113 virtio_input_evdevs: Vec<PathBuf>,
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800114 split_irqchip: bool,
Dylan Reid059a1882018-07-23 17:58:09 -0700115}
116
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700117impl Default for Config {
118 fn default() -> Config {
119 Config {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700120 vcpu_count: None,
Daniel Verkamp107edb32019-04-05 09:58:48 -0700121 vcpu_affinity: Vec::new(),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700122 memory: None,
123 kernel_path: PathBuf::default(),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800124 android_fstab: None,
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800125 initrd_path: None,
Zach Reiznerbb678712018-01-30 18:13:04 -0800126 params: Vec::new(),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700127 socket_path: None,
Zach Reizner8864cb02018-01-16 17:59:03 -0800128 plugin: None,
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700129 plugin_root: None,
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800130 plugin_mounts: Vec::new(),
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -0800131 plugin_gid_maps: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700132 disks: Vec::new(),
133 host_ip: None,
134 netmask: None,
135 mac_address: None,
136 vhost_net: false,
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800137 tap_fd: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700138 cid: None,
139 gpu: false,
David Tolnay43f8e212019-02-13 17:28:16 -0800140 software_tpm: false,
Daniel Verkampaac28132018-10-15 14:58:48 -0700141 wayland_socket_path: None,
142 wayland_dmabuf: false,
143 shared_dirs: Vec::new(),
Lepton Wu9105e9f2019-03-14 11:38:31 -0700144 sandbox: !cfg!(feature = "default-no-sandbox"),
Daniel Verkampaac28132018-10-15 14:58:48 -0700145 seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
paulhsiaf052cfe2019-01-22 15:22:25 +0800146 cras_audio: false,
Dylan Reid3082e8e2019-01-07 10:33:48 -0800147 null_audio: false,
Trent Begin17ccaad2019-04-17 13:51:25 -0600148 serial_parameters: BTreeMap::new(),
149 syslog_tag: None,
Jorge E. Moreira99d3f082019-03-07 10:59:54 -0800150 virtio_single_touch: None,
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800151 virtio_trackpad: None,
152 virtio_mouse: None,
153 virtio_keyboard: None,
154 virtio_input_evdevs: Vec::new(),
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800155 split_irqchip: false,
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700156 }
157 }
158}
159
Stephen Barbera00753b2017-07-18 13:57:26 -0700160// Wait for all children to exit. Return true if they have all exited, false
161// otherwise.
162fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -0700163 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -0700164 const CHILD_WAIT_MS: u64 = 10;
165 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -0700166 loop {
Zach Reizner56158c82017-08-24 13:50:14 -0700167 match reap_child() {
168 Ok(0) => break,
169 // We expect ECHILD which indicates that there were no children left.
170 Err(e) if e.errno() == libc::ECHILD => return true,
171 Err(e) => {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800172 warn!("error while waiting for children: {}", e);
Zach Reizner56158c82017-08-24 13:50:14 -0700173 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -0700174 }
Zach Reizner56158c82017-08-24 13:50:14 -0700175 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -0700176 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -0700177 }
178 }
Zach Reizner56158c82017-08-24 13:50:14 -0700179 // There's no timeout option for waitpid which reap_child calls internally, so our only
180 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -0700181 sleep(Duration::from_millis(CHILD_WAIT_MS));
182 }
183
184 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -0800185 false
Stephen Barbera00753b2017-07-18 13:57:26 -0700186}
187
Daniel Verkamp107edb32019-04-05 09:58:48 -0700188/// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
189fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
190 let mut cpuset = Vec::new();
191 for part in s.split(',') {
192 let range: Vec<&str> = part.split('-').collect();
193 if range.len() == 0 || range.len() > 2 {
194 return Err(argument::Error::InvalidValue {
195 value: part.to_owned(),
196 expected: "invalid list syntax",
197 });
198 }
199 let first_cpu: usize = range[0]
200 .parse()
201 .map_err(|_| argument::Error::InvalidValue {
202 value: part.to_owned(),
203 expected: "CPU index must be a non-negative integer",
204 })?;
205 let last_cpu: usize = if range.len() == 2 {
206 range[1]
207 .parse()
208 .map_err(|_| argument::Error::InvalidValue {
209 value: part.to_owned(),
210 expected: "CPU index must be a non-negative integer",
211 })?
212 } else {
213 first_cpu
214 };
215
216 if last_cpu < first_cpu {
217 return Err(argument::Error::InvalidValue {
218 value: part.to_owned(),
219 expected: "CPU ranges must be from low to high",
220 });
221 }
222
223 for cpu in first_cpu..=last_cpu {
224 cpuset.push(cpu);
225 }
226 }
227 Ok(cpuset)
228}
229
Trent Begin17ccaad2019-04-17 13:51:25 -0600230fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
231 let mut serial_setting = SerialParameters {
232 type_: SerialType::Sink,
233 path: None,
234 num: 0,
235 console: false,
236 };
237
238 let opts = s
239 .split(",")
240 .map(|frag| frag.split("="))
241 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
242
243 for (k, v) in opts {
244 match k {
245 "type" => {
246 serial_setting.type_ = v
247 .parse::<SerialType>()
248 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
249 }
250 "num" => {
251 let num = v.parse::<u8>().map_err(|e| {
252 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
253 })?;
254 if num < 1 || num > 4 {
255 return Err(argument::Error::InvalidValue {
256 value: num.to_string(),
257 expected: "Serial port num must be between 1 - 4",
258 });
259 }
260 serial_setting.num = num;
261 }
262 "console" => {
263 serial_setting.console = v.parse::<bool>().map_err(|e| {
264 argument::Error::Syntax(format!(
265 "serial device console is not parseable: {}",
266 e
267 ))
268 })?
269 }
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700270 "path" => serial_setting.path = Some(PathBuf::from(v)),
Trent Begin17ccaad2019-04-17 13:51:25 -0600271 _ => {
272 return Err(argument::Error::UnknownArgument(format!(
273 "serial parameter {}",
274 k
275 )));
276 }
277 }
278 }
279
280 Ok(serial_setting)
281}
282
Zach Reiznerefe95782017-08-26 18:05:48 -0700283fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
284 match name {
285 "" => {
Zach Reizner8864cb02018-01-16 17:59:03 -0800286 if cfg.plugin.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700287 return Err(argument::Error::TooManyArguments(
288 "`plugin` can not be used with kernel".to_owned(),
289 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800290 } else if !cfg.kernel_path.as_os_str().is_empty() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700291 return Err(argument::Error::TooManyArguments(
292 "expected exactly one kernel path".to_owned(),
293 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700294 } else {
295 let kernel_path = PathBuf::from(value.unwrap());
296 if !kernel_path.exists() {
297 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700298 value: value.unwrap().to_owned(),
299 expected: "this kernel path does not exist",
300 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700301 }
302 cfg.kernel_path = kernel_path;
303 }
304 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800305 "android-fstab" => {
306 if cfg.android_fstab.is_some()
307 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
308 {
309 return Err(argument::Error::TooManyArguments(
310 "expected exactly one android fstab path".to_owned(),
311 ));
312 } else {
313 let android_fstab = PathBuf::from(value.unwrap());
314 if !android_fstab.exists() {
315 return Err(argument::Error::InvalidValue {
316 value: value.unwrap().to_owned(),
317 expected: "this android fstab path does not exist",
318 });
319 }
320 cfg.android_fstab = Some(android_fstab);
321 }
322 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700323 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -0800324 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -0700325 }
326 "cpus" => {
327 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700328 return Err(argument::Error::TooManyArguments(
329 "`cpus` already given".to_owned(),
330 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700331 }
332 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -0700333 Some(
334 value
335 .unwrap()
336 .parse()
337 .map_err(|_| argument::Error::InvalidValue {
338 value: value.unwrap().to_owned(),
339 expected: "this value for `cpus` needs to be integer",
340 })?,
341 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700342 }
Daniel Verkamp107edb32019-04-05 09:58:48 -0700343 "cpu-affinity" => {
344 if cfg.vcpu_affinity.len() != 0 {
345 return Err(argument::Error::TooManyArguments(
346 "`cpu-affinity` already given".to_owned(),
347 ));
348 }
349 cfg.vcpu_affinity = parse_cpu_set(value.unwrap())?;
350 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700351 "mem" => {
352 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700353 return Err(argument::Error::TooManyArguments(
354 "`mem` already given".to_owned(),
355 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700356 }
357 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -0700358 Some(
359 value
360 .unwrap()
361 .parse()
362 .map_err(|_| argument::Error::InvalidValue {
363 value: value.unwrap().to_owned(),
364 expected: "this value for `mem` needs to be integer",
365 })?,
366 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700367 }
paulhsiaf052cfe2019-01-22 15:22:25 +0800368 "cras-audio" => {
369 cfg.cras_audio = true;
370 }
Dylan Reid3082e8e2019-01-07 10:33:48 -0800371 "null-audio" => {
372 cfg.null_audio = true;
373 }
Trent Begin17ccaad2019-04-17 13:51:25 -0600374 "serial" => {
375 let serial_params = parse_serial_options(value.unwrap())?;
376 let num = serial_params.num;
377 if cfg.serial_parameters.contains_key(&num) {
378 return Err(argument::Error::TooManyArguments(format!(
379 "serial num {}",
380 num
381 )));
382 }
383
384 if serial_params.console {
385 for (_, params) in &cfg.serial_parameters {
386 if params.console {
387 return Err(argument::Error::TooManyArguments(format!(
388 "serial device {} already set as console",
389 params.num
390 )));
391 }
392 }
393 }
394
395 cfg.serial_parameters.insert(num, serial_params);
396 }
397 "syslog-tag" => {
398 if cfg.syslog_tag.is_some() {
399 return Err(argument::Error::TooManyArguments(
400 "`syslog-tag` already given".to_owned(),
401 ));
402 }
403 syslog::set_proc_name(value.unwrap());
404 cfg.syslog_tag = Some(value.unwrap().to_owned());
405 }
Dylan Reid88624f82018-01-11 09:20:16 -0800406 "root" | "disk" | "rwdisk" | "qcow" | "rwqcow" => {
Zach Reiznerefe95782017-08-26 18:05:48 -0700407 let disk_path = PathBuf::from(value.unwrap());
408 if !disk_path.exists() {
409 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700410 value: value.unwrap().to_owned(),
411 expected: "this disk path does not exist",
412 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700413 }
414 if name == "root" {
Daniel Verkampaac28132018-10-15 14:58:48 -0700415 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -0700416 return Err(argument::Error::TooManyArguments(
417 "ran out of letters for to assign to root disk".to_owned(),
418 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700419 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700420 cfg.params.push(format!(
421 "root=/dev/vd{} ro",
David Tolnay5bbbf612018-12-01 17:49:30 -0800422 char::from(b'a' + cfg.disks.len() as u8)
Zach Reizner55a9e502018-10-03 10:22:32 -0700423 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700424 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700425 cfg.disks.push(DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -0700426 path: disk_path,
427 read_only: !name.starts_with("rw"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700428 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700429 }
430 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700431 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700432 return Err(argument::Error::TooManyArguments(
433 "`host_ip` already given".to_owned(),
434 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700435 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700436 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -0700437 Some(
438 value
439 .unwrap()
440 .parse()
441 .map_err(|_| argument::Error::InvalidValue {
442 value: value.unwrap().to_owned(),
443 expected: "`host_ip` needs to be in the form \"x.x.x.x\"",
444 })?,
445 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700446 }
447 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700448 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700449 return Err(argument::Error::TooManyArguments(
450 "`netmask` already given".to_owned(),
451 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700452 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700453 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -0700454 Some(
455 value
456 .unwrap()
457 .parse()
458 .map_err(|_| argument::Error::InvalidValue {
459 value: value.unwrap().to_owned(),
460 expected: "`netmask` needs to be in the form \"x.x.x.x\"",
461 })?,
462 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700463 }
464 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700465 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700466 return Err(argument::Error::TooManyArguments(
467 "`mac` already given".to_owned(),
468 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700469 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700470 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -0700471 Some(
472 value
473 .unwrap()
474 .parse()
475 .map_err(|_| argument::Error::InvalidValue {
476 value: value.unwrap().to_owned(),
477 expected: "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
478 })?,
479 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700480 }
Stephen Barber28a5a612017-10-20 17:15:30 -0700481 "wayland-sock" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700482 if cfg.wayland_socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700483 return Err(argument::Error::TooManyArguments(
484 "`wayland-sock` already given".to_owned(),
485 ));
Stephen Barber28a5a612017-10-20 17:15:30 -0700486 }
487 let wayland_socket_path = PathBuf::from(value.unwrap());
488 if !wayland_socket_path.exists() {
489 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700490 value: value.unwrap().to_string(),
491 expected: "Wayland socket does not exist",
492 });
Stephen Barber28a5a612017-10-20 17:15:30 -0700493 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700494 cfg.wayland_socket_path = Some(wayland_socket_path);
Stephen Barber28a5a612017-10-20 17:15:30 -0700495 }
David Reveman52ba4e52018-04-22 21:42:09 -0400496 #[cfg(feature = "wl-dmabuf")]
Daniel Verkampaac28132018-10-15 14:58:48 -0700497 "wayland-dmabuf" => cfg.wayland_dmabuf = true,
Zach Reiznerefe95782017-08-26 18:05:48 -0700498 "socket" => {
499 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700500 return Err(argument::Error::TooManyArguments(
501 "`socket` already given".to_owned(),
502 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700503 }
504 let mut socket_path = PathBuf::from(value.unwrap());
505 if socket_path.is_dir() {
506 socket_path.push(format!("crosvm-{}.sock", getpid()));
507 }
508 if socket_path.exists() {
509 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700510 value: socket_path.to_string_lossy().into_owned(),
511 expected: "this socket path already exists",
512 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700513 }
514 cfg.socket_path = Some(socket_path);
515 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700516 "disable-sandbox" => {
Lepton Wu9105e9f2019-03-14 11:38:31 -0700517 cfg.sandbox = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700518 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700519 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700520 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700521 return Err(argument::Error::TooManyArguments(
522 "`cid` alread given".to_owned(),
523 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700524 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700525 cfg.cid = Some(
526 value
527 .unwrap()
528 .parse()
529 .map_err(|_| argument::Error::InvalidValue {
530 value: value.unwrap().to_owned(),
531 expected: "this value for `cid` must be an unsigned integer",
532 })?,
533 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700534 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700535 "shared-dir" => {
536 // Formatted as <src:tag>.
537 let param = value.unwrap();
538 let mut components = param.splitn(2, ':');
Zach Reizner55a9e502018-10-03 10:22:32 -0700539 let src =
540 PathBuf::from(
541 components
542 .next()
543 .ok_or_else(|| argument::Error::InvalidValue {
544 value: param.to_owned(),
545 expected: "missing source path for `shared-dir`",
546 })?,
547 );
548 let tag = components
549 .next()
550 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700551 value: param.to_owned(),
552 expected: "missing tag for `shared-dir`",
David Tolnay2bac1e72018-12-12 14:33:42 -0800553 })?
554 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700555
556 if !src.is_dir() {
557 return Err(argument::Error::InvalidValue {
558 value: param.to_owned(),
559 expected: "source path for `shared-dir` must be a directory",
560 });
561 }
562
Daniel Verkampaac28132018-10-15 14:58:48 -0700563 cfg.shared_dirs.push((src, tag));
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700564 }
Dylan Reide026ef02017-10-02 19:03:52 -0700565 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700566 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -0700567 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -0700568 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800569 "plugin" => {
570 if !cfg.kernel_path.as_os_str().is_empty() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700571 return Err(argument::Error::TooManyArguments(
572 "`plugin` can not be used with kernel".to_owned(),
573 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800574 } else if cfg.plugin.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700575 return Err(argument::Error::TooManyArguments(
576 "`plugin` already given".to_owned(),
577 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800578 }
Zach Reiznercc30d582018-01-23 21:16:42 -0800579 let plugin = PathBuf::from(value.unwrap().to_owned());
580 if plugin.is_relative() {
581 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700582 value: plugin.to_string_lossy().into_owned(),
583 expected: "the plugin path must be an absolute path",
584 });
Zach Reiznercc30d582018-01-23 21:16:42 -0800585 }
586 cfg.plugin = Some(plugin);
Zach Reizner55a9e502018-10-03 10:22:32 -0700587 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700588 "plugin-root" => {
589 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -0700590 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800591 "plugin-mount" => {
592 let components: Vec<&str> = value.unwrap().split(":").collect();
593 if components.len() != 3 {
594 return Err(argument::Error::InvalidValue {
595 value: value.unwrap().to_owned(),
596 expected:
597 "`plugin-mount` must have exactly 3 components: <src>:<dst>:<writable>",
598 });
599 }
600
601 let src = PathBuf::from(components[0]);
602 if src.is_relative() {
603 return Err(argument::Error::InvalidValue {
604 value: components[0].to_owned(),
605 expected: "the source path for `plugin-mount` must be absolute",
606 });
607 }
608 if !src.exists() {
609 return Err(argument::Error::InvalidValue {
610 value: components[0].to_owned(),
611 expected: "the source path for `plugin-mount` does not exist",
612 });
613 }
614
615 let dst = PathBuf::from(components[1]);
616 if dst.is_relative() {
617 return Err(argument::Error::InvalidValue {
618 value: components[1].to_owned(),
619 expected: "the destination path for `plugin-mount` must be absolute",
620 });
621 }
622
623 let writable: bool =
624 components[2]
625 .parse()
626 .map_err(|_| argument::Error::InvalidValue {
627 value: components[2].to_owned(),
628 expected: "the <writable> component for `plugin-mount` is not valid bool",
629 })?;
630
631 cfg.plugin_mounts.push(BindMount { src, dst, writable });
632 }
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -0800633 "plugin-gid-map" => {
634 let components: Vec<&str> = value.unwrap().split(":").collect();
635 if components.len() != 3 {
636 return Err(argument::Error::InvalidValue {
637 value: value.unwrap().to_owned(),
638 expected:
639 "`plugin-gid-map` must have exactly 3 components: <inner>:<outer>:<count>",
640 });
641 }
642
643 let inner: libc::gid_t =
644 components[0]
645 .parse()
646 .map_err(|_| argument::Error::InvalidValue {
647 value: components[0].to_owned(),
648 expected: "the <inner> component for `plugin-gid-map` is not valid gid",
649 })?;
650
651 let outer: libc::gid_t =
652 components[1]
653 .parse()
654 .map_err(|_| argument::Error::InvalidValue {
655 value: components[1].to_owned(),
656 expected: "the <outer> component for `plugin-gid-map` is not valid gid",
657 })?;
658
659 let count: u32 = components[2]
660 .parse()
661 .map_err(|_| argument::Error::InvalidValue {
662 value: components[2].to_owned(),
663 expected: "the <count> component for `plugin-gid-map` is not valid number",
664 })?;
665
666 cfg.plugin_gid_maps.push(GidMap {
667 inner,
668 outer,
669 count,
670 });
671 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700672 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700673 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800674 cfg.tap_fd.push(
675 value
676 .unwrap()
677 .parse()
678 .map_err(|_| argument::Error::InvalidValue {
679 value: value.unwrap().to_owned(),
680 expected: "this value for `tap-fd` must be an unsigned integer",
681 })?,
682 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700683 }
Zach Reizner3a8100a2017-09-13 19:15:43 -0700684 "gpu" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700685 cfg.gpu = true;
Zach Reizner3a8100a2017-09-13 19:15:43 -0700686 }
David Tolnay43f8e212019-02-13 17:28:16 -0800687 "software-tpm" => {
688 cfg.software_tpm = true;
689 }
Jorge E. Moreira99d3f082019-03-07 10:59:54 -0800690 "single-touch" => {
691 if cfg.virtio_single_touch.is_some() {
692 return Err(argument::Error::TooManyArguments(
693 "`single-touch` already given".to_owned(),
694 ));
695 }
696 let mut it = value.unwrap().split(":");
697
698 let mut single_touch_spec =
699 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
700 if let Some(width) = it.next() {
701 single_touch_spec.width = width.trim().parse().unwrap();
702 }
703 if let Some(height) = it.next() {
704 single_touch_spec.height = height.trim().parse().unwrap();
705 }
706
707 cfg.virtio_single_touch = Some(single_touch_spec);
708 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800709 "trackpad" => {
710 if cfg.virtio_trackpad.is_some() {
711 return Err(argument::Error::TooManyArguments(
712 "`trackpad` already given".to_owned(),
713 ));
714 }
715 let mut it = value.unwrap().split(":");
716
717 let mut trackpad_spec =
Jorge E. Moreira99d3f082019-03-07 10:59:54 -0800718 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800719 if let Some(width) = it.next() {
720 trackpad_spec.width = width.trim().parse().unwrap();
721 }
722 if let Some(height) = it.next() {
723 trackpad_spec.height = height.trim().parse().unwrap();
724 }
725
726 cfg.virtio_trackpad = Some(trackpad_spec);
727 }
728 "mouse" => {
729 if cfg.virtio_mouse.is_some() {
730 return Err(argument::Error::TooManyArguments(
731 "`mouse` already given".to_owned(),
732 ));
733 }
734 cfg.virtio_mouse = Some(PathBuf::from(value.unwrap().to_owned()));
735 }
736 "keyboard" => {
737 if cfg.virtio_keyboard.is_some() {
738 return Err(argument::Error::TooManyArguments(
739 "`keyboard` already given".to_owned(),
740 ));
741 }
742 cfg.virtio_keyboard = Some(PathBuf::from(value.unwrap().to_owned()));
743 }
744 "evdev" => {
745 let dev_path = PathBuf::from(value.unwrap());
746 if !dev_path.exists() {
747 return Err(argument::Error::InvalidValue {
748 value: value.unwrap().to_owned(),
749 expected: "this input device path does not exist",
750 });
751 }
752 cfg.virtio_input_evdevs.push(dev_path);
753 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800754 "split-irqchip" => {
755 cfg.split_irqchip = true;
756 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800757 "initrd" => {
758 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
759 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700760 "help" => return Err(argument::Error::PrintHelp),
761 _ => unreachable!(),
762 }
763 Ok(())
764}
765
Dylan Reidbfba9932018-02-05 15:51:59 -0800766fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -0700767 let arguments =
768 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800769 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800770 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700771 Argument::short_value('p',
772 "params",
773 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -0800774 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700775 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
Daniel Verkamp107edb32019-04-05 09:58:48 -0700776 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 -0700777 Argument::short_value('m',
778 "mem",
779 "N",
780 "Amount of guest memory in MiB. (default: 256)"),
781 Argument::short_value('r',
782 "root",
783 "PATH",
784 "Path to a root disk image. Like `--disk` but adds appropriate kernel command line option."),
785 Argument::short_value('d', "disk", "PATH", "Path to a disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700786 Argument::value("qcow", "PATH", "Path to a qcow2 disk image. (Deprecated; use --disk instead.)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700787 Argument::value("rwdisk", "PATH", "Path to a writable disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700788 Argument::value("rwqcow", "PATH", "Path to a writable qcow2 disk image. (Deprecated; use --rwdisk instead.)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700789 Argument::value("host_ip",
790 "IP",
791 "IP address to assign to host tap interface."),
792 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
793 Argument::value("mac", "MAC", "MAC address for VM."),
paulhsiaf052cfe2019-01-22 15:22:25 +0800794 Argument::flag("cras-audio", "Add an audio device to the VM that plays samples through CRAS server"),
Dylan Reid3082e8e2019-01-07 10:33:48 -0800795 Argument::flag("null-audio", "Add an audio device to the VM that plays samples to /dev/null"),
Trent Begin17ccaad2019-04-17 13:51:25 -0600796 Argument::value("serial",
797 "type=TYPE,[path=PATH,num=NUM,console]",
798 "Comma seperated key=value pairs for setting up serial devices. Can be given more than once.
799 Possible key values:
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700800 type=(stdout,syslog,sink,file) - Where to route the serial device
Trent Begin17ccaad2019-04-17 13:51:25 -0600801 num=(1,2,3,4) - Serial Device Number
Jorge E. Moreira9c9e0e72019-05-17 13:57:04 -0700802 path=PATH - The path to the file to write to when type=file
Trent Begin17ccaad2019-04-17 13:51:25 -0600803 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
804 "),
805 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
Stephen Barber28a5a612017-10-20 17:15:30 -0700806 Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
David Reveman52ba4e52018-04-22 21:42:09 -0400807 #[cfg(feature = "wl-dmabuf")]
808 Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700809 Argument::short_value('s',
810 "socket",
811 "PATH",
812 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700813 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700814 Argument::value("cid", "CID", "Context ID for virtual sockets."),
815 Argument::value("shared-dir", "PATH:TAG",
816 "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 -0700817 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner8864cb02018-01-16 17:59:03 -0800818 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -0800819 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -0800820 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700821 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 -0800822 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800823 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 -0800824 #[cfg(feature = "plugin")]
825 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 +0000826 Argument::flag("vhost-net", "Use vhost for networking."),
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700827 Argument::value("tap-fd",
828 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800829 "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 -0700830 #[cfg(feature = "gpu")]
831 Argument::flag("gpu", "(EXPERIMENTAL) enable virtio-gpu device"),
David Tolnay43f8e212019-02-13 17:28:16 -0800832 #[cfg(feature = "tpm")]
833 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800834 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 -0800835 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 -0800836 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)."),
837 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
838 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 -0800839 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
840 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700841 Argument::short_flag('h', "help", "Print help message.")];
842
843 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -0700844 let match_res = set_arguments(args, &arguments[..], |name, value| {
845 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -0800846 })
847 .and_then(|_| {
Zach Reizner8864cb02018-01-16 17:59:03 -0800848 if cfg.kernel_path.as_os_str().is_empty() && cfg.plugin.is_none() {
Zach Reiznerefe95782017-08-26 18:05:48 -0700849 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
850 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700851 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
852 if cfg.host_ip.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700853 return Err(argument::Error::ExpectedArgument(
854 "`host_ip` missing from network config".to_owned(),
855 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700856 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700857 if cfg.netmask.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700858 return Err(argument::Error::ExpectedArgument(
859 "`netmask` missing from network config".to_owned(),
860 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700861 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700862 if cfg.mac_address.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700863 return Err(argument::Error::ExpectedArgument(
864 "`mac` missing from network config".to_owned(),
865 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700866 }
867 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700868 if cfg.plugin_root.is_some() && cfg.plugin.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700869 return Err(argument::Error::ExpectedArgument(
870 "`plugin-root` requires `plugin`".to_owned(),
871 ));
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700872 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700873 Ok(())
874 });
875
876 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -0800877 #[cfg(feature = "plugin")]
David Tolnay2bac1e72018-12-12 14:33:42 -0800878 Ok(()) if cfg.plugin.is_some() => match plugin::run_config(cfg) {
879 Ok(_) => {
880 info!("crosvm and plugin have exited normally");
881 Ok(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800882 }
David Tolnay2bac1e72018-12-12 14:33:42 -0800883 Err(e) => {
884 error!("{}", e);
885 Err(())
886 }
887 },
Zach Reizner55a9e502018-10-03 10:22:32 -0700888 Ok(()) => match linux::run_config(cfg) {
889 Ok(_) => {
890 info!("crosvm has exited normally");
891 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -0700892 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700893 Err(e) => {
894 error!("{}", e);
895 Err(())
896 }
897 },
Dylan Reidbfba9932018-02-05 15:51:59 -0800898 Err(argument::Error::PrintHelp) => {
899 print_help("crosvm run", "KERNEL", &arguments[..]);
900 Ok(())
901 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800902 Err(e) => {
903 println!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -0800904 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800905 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700906 }
907}
908
Jingkui Wang100e6e42019-03-08 20:41:57 -0800909fn handle_request(
910 request: &VmRequest,
911 args: std::env::Args,
912) -> std::result::Result<VmResponse, ()> {
913 let mut return_result = Err(());
Zach Reiznerefe95782017-08-26 18:05:48 -0700914 for socket_path in args {
Zach Reiznera60744b2019-02-13 17:33:32 -0800915 match UnixSeqpacket::connect(&socket_path) {
Zach Reiznerefe95782017-08-26 18:05:48 -0700916 Ok(s) => {
Jakub Starone7c59052019-04-09 12:31:14 -0700917 let socket: VmControlRequestSocket = MsgSocket::new(s);
Zach Reizner78986322019-02-21 20:43:21 -0800918 if let Err(e) = socket.send(request) {
Zach Reizner55a9e502018-10-03 10:22:32 -0700919 error!(
David Tolnayb4bd00f2019-02-12 17:51:26 -0800920 "failed to send request to socket at '{}': {}",
Zach Reizner55a9e502018-10-03 10:22:32 -0700921 socket_path, e
922 );
Zach Reizner78986322019-02-21 20:43:21 -0800923 return_result = Err(());
924 continue;
Zach Reiznerefe95782017-08-26 18:05:48 -0700925 }
Zach Reizner78986322019-02-21 20:43:21 -0800926 match socket.recv() {
Jingkui Wang100e6e42019-03-08 20:41:57 -0800927 Ok(response) => return_result = Ok(response),
Zach Reizner78986322019-02-21 20:43:21 -0800928 Err(e) => {
929 error!(
930 "failed to send request to socket at2 '{}': {}",
931 socket_path, e
932 );
933 return_result = Err(());
934 continue;
935 }
Dylan Reidd4432042017-12-06 18:20:09 -0800936 }
937 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800938 Err(e) => {
939 error!("failed to connect to socket at '{}': {}", socket_path, e);
940 return_result = Err(());
941 }
Dylan Reidd4432042017-12-06 18:20:09 -0800942 }
943 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800944
945 return_result
Dylan Reidd4432042017-12-06 18:20:09 -0800946}
Zach Reiznerefe95782017-08-26 18:05:48 -0700947
Jingkui Wang100e6e42019-03-08 20:41:57 -0800948fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()> {
949 let response = handle_request(request, args)?;
950 info!("request response was {}", response);
951 Ok(())
952}
953
Zach Reizner78986322019-02-21 20:43:21 -0800954fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> {
955 if args.len() == 0 {
956 print_help("crosvm stop", "VM_SOCKET...", &[]);
957 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800958 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -0800959 }
960 vms_request(&VmRequest::Exit, args)
961}
962
963fn suspend_vms(args: std::env::Args) -> std::result::Result<(), ()> {
964 if args.len() == 0 {
965 print_help("crosvm suspend", "VM_SOCKET...", &[]);
966 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800967 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -0800968 }
969 vms_request(&VmRequest::Suspend, args)
970}
971
972fn resume_vms(args: std::env::Args) -> std::result::Result<(), ()> {
973 if args.len() == 0 {
974 print_help("crosvm resume", "VM_SOCKET...", &[]);
975 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800976 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -0800977 }
978 vms_request(&VmRequest::Resume, args)
979}
980
981fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
982 if args.len() < 2 {
983 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
984 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800985 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -0800986 }
987 let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
988 Ok(n) => n,
989 Err(_) => {
990 error!("Failed to parse number of bytes");
991 return Err(());
992 }
993 };
994
Jakub Staron1f828d72019-04-11 12:49:29 -0700995 let command = BalloonControlCommand::Adjust { num_bytes };
996 vms_request(&VmRequest::BalloonCommand(command), args)
Zach Reizner78986322019-02-21 20:43:21 -0800997}
998
Dylan Reid2dcb6322018-07-13 10:42:48 -0700999fn create_qcow2(mut args: std::env::Args) -> std::result::Result<(), ()> {
1000 if args.len() != 2 {
1001 print_help("crosvm create_qcow2", "PATH SIZE", &[]);
Dylan Reid940259c2018-07-20 14:22:33 -07001002 println!("Create a new QCOW2 image at `PATH` of the specified `SIZE` in bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001003 return Err(());
Dylan Reid2dcb6322018-07-13 10:42:48 -07001004 }
1005 let file_path = args.nth(0).unwrap();
1006 let size: u64 = match args.nth(0).unwrap().parse::<u64>() {
1007 Ok(n) => n,
1008 Err(_) => {
1009 error!("Failed to parse size of the disk.");
1010 return Err(());
Zach Reizner55a9e502018-10-03 10:22:32 -07001011 }
Dylan Reid2dcb6322018-07-13 10:42:48 -07001012 };
1013
1014 let file = OpenOptions::new()
1015 .create(true)
1016 .read(true)
1017 .write(true)
1018 .open(&file_path)
1019 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08001020 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -07001021 })?;
1022
Zach Reizner55a9e502018-10-03 10:22:32 -07001023 QcowFile::new(file, size).map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -08001024 error!("Failed to create qcow file at '{}': {}", file_path, e);
Zach Reizner55a9e502018-10-03 10:22:32 -07001025 })?;
Dylan Reid2dcb6322018-07-13 10:42:48 -07001026
1027 Ok(())
1028}
1029
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001030fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
1031 if args.len() < 2 {
1032 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
1033 println!("Manage attached virtual disk devices.");
1034 println!("Subcommands:");
1035 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Jianxun Zhang56497d22019-03-04 14:38:24 -08001036 return Err(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001037 }
1038 let subcommand: &str = &args.nth(0).unwrap();
1039
1040 let request = match subcommand {
1041 "resize" => {
1042 let disk_index = match args.nth(0).unwrap().parse::<usize>() {
1043 Ok(n) => n,
1044 Err(_) => {
1045 error!("Failed to parse disk index");
1046 return Err(());
1047 }
1048 };
1049
1050 let new_size = match args.nth(0).unwrap().parse::<u64>() {
1051 Ok(n) => n,
1052 Err(_) => {
1053 error!("Failed to parse disk size");
1054 return Err(());
1055 }
1056 };
1057
Jakub Staronecf81e02019-04-11 11:43:39 -07001058 VmRequest::DiskCommand {
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001059 disk_index,
Jakub Staronecf81e02019-04-11 11:43:39 -07001060 command: DiskControlCommand::Resize { new_size },
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001061 }
1062 }
1063 _ => {
1064 error!("Unknown disk subcommand '{}'", subcommand);
1065 return Err(());
1066 }
1067 };
1068
Zach Reizner78986322019-02-21 20:43:21 -08001069 vms_request(&request, args)
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001070}
1071
Jingkui Wang100e6e42019-03-08 20:41:57 -08001072enum ModifyUsbError {
1073 ArgMissing(&'static str),
1074 ArgParse(&'static str, String),
1075 ArgParseInt(&'static str, String, ParseIntError),
1076 FailedFdValidate(sys_util::Error),
1077 PathDoesNotExist(PathBuf),
1078 SocketFailed,
1079 UnexpectedResponse(VmResponse),
1080 UnknownCommand(String),
1081 UsbControl(UsbControlResult),
1082}
1083
1084impl fmt::Display for ModifyUsbError {
1085 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1086 use self::ModifyUsbError::*;
1087
1088 match self {
1089 ArgMissing(a) => write!(f, "argument missing: {}", a),
1090 ArgParse(name, value) => {
1091 write!(f, "failed to parse argument {} value `{}`", name, value)
1092 }
1093 ArgParseInt(name, value, e) => write!(
1094 f,
1095 "failed to parse integer argument {} value `{}`: {}",
1096 name, value, e
1097 ),
1098 FailedFdValidate(e) => write!(f, "failed to validate file descriptor: {}", e),
1099 PathDoesNotExist(p) => write!(f, "path `{}` does not exist", p.display()),
1100 SocketFailed => write!(f, "socket failed"),
1101 UnexpectedResponse(r) => write!(f, "unexpected response: {}", r),
1102 UnknownCommand(c) => write!(f, "unknown command: `{}`", c),
1103 UsbControl(e) => write!(f, "{}", e),
1104 }
1105 }
1106}
1107
1108type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
1109
1110fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
1111 debug!("parse_bus_id_addr: {}", v);
1112 let mut ids = v.split(":");
1113 match (ids.next(), ids.next(), ids.next(), ids.next()) {
1114 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
1115 let bus_id = bus_id
1116 .parse::<u8>()
1117 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
1118 let addr = addr
1119 .parse::<u8>()
1120 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
1121 let vid = u16::from_str_radix(&vid, 16)
1122 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
1123 let pid = u16::from_str_radix(&pid, 16)
1124 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
1125 Ok((bus_id, addr, vid, pid))
1126 }
1127 _ => Err(ModifyUsbError::ArgParse(
1128 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
1129 v.to_owned(),
1130 )),
1131 }
1132}
1133
1134fn raw_fd_from_path(path: &Path) -> ModifyUsbResult<RawFd> {
1135 if !path.exists() {
1136 return Err(ModifyUsbError::PathDoesNotExist(path.to_owned()));
1137 }
1138 let raw_fd = path
1139 .file_name()
1140 .and_then(|fd_osstr| fd_osstr.to_str())
1141 .map_or(
1142 Err(ModifyUsbError::ArgParse(
1143 "USB_DEVICE_PATH",
1144 path.to_string_lossy().into_owned(),
1145 )),
1146 |fd_str| {
1147 fd_str.parse::<libc::c_int>().map_err(|e| {
1148 ModifyUsbError::ArgParseInt("USB_DEVICE_PATH", fd_str.to_owned(), e)
1149 })
1150 },
1151 )?;
David Tolnay5fb3f512019-04-12 19:22:33 -07001152 validate_raw_fd(raw_fd).map_err(ModifyUsbError::FailedFdValidate)
Jingkui Wang100e6e42019-03-08 20:41:57 -08001153}
1154
1155fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
1156 let val = args
1157 .next()
1158 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
1159 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
1160 let dev_path = PathBuf::from(
1161 args.next()
1162 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
1163 );
1164 let usb_file: Option<File> = if dev_path == Path::new("-") {
1165 None
1166 } else if dev_path.parent() == Some(Path::new("/proc/self/fd")) {
1167 // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
1168 // Safe because we will validate |raw_fd|.
1169 Some(unsafe { File::from_raw_fd(raw_fd_from_path(&dev_path)?) })
1170 } else {
1171 Some(
1172 OpenOptions::new()
1173 .read(true)
1174 .write(true)
1175 .open(&dev_path)
1176 .map_err(|_| ModifyUsbError::UsbControl(UsbControlResult::FailedToOpenDevice))?,
1177 )
1178 };
1179
1180 let request = VmRequest::UsbCommand(UsbControlCommand::AttachDevice {
1181 bus,
1182 addr,
1183 vid,
1184 pid,
David Tolnay5fb3f512019-04-12 19:22:33 -07001185 fd: usb_file.map(MaybeOwnedFd::Owned),
Jingkui Wang100e6e42019-03-08 20:41:57 -08001186 });
1187 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
1188 match response {
1189 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
1190 r => Err(ModifyUsbError::UnexpectedResponse(r)),
1191 }
1192}
1193
1194fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
1195 let port: u8 = args
1196 .next()
1197 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
1198 p.parse::<u8>()
1199 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
1200 })?;
1201 let request = VmRequest::UsbCommand(UsbControlCommand::DetachDevice { port });
1202 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
1203 match response {
1204 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
1205 r => Err(ModifyUsbError::UnexpectedResponse(r)),
1206 }
1207}
1208
Zach Reizneraff94ca2019-03-18 20:58:31 -07001209fn usb_list(args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
1210 let mut ports: [u8; USB_CONTROL_MAX_PORTS] = Default::default();
1211 for (index, port) in ports.iter_mut().enumerate() {
1212 *port = index as u8
1213 }
1214 let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { ports });
Jingkui Wang100e6e42019-03-08 20:41:57 -08001215 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
1216 match response {
1217 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
1218 r => Err(ModifyUsbError::UnexpectedResponse(r)),
1219 }
1220}
1221
1222fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reizneraff94ca2019-03-18 20:58:31 -07001223 if args.len() < 2 {
Jingkui Wang100e6e42019-03-08 20:41:57 -08001224 print_help("crosvm usb",
Zach Reizneraff94ca2019-03-18 20:58:31 -07001225 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
Jingkui Wang100e6e42019-03-08 20:41:57 -08001226 return Err(());
1227 }
1228
1229 // This unwrap will not panic because of the above length check.
1230 let command = args.next().unwrap();
1231 let result = match command.as_ref() {
1232 "attach" => usb_attach(args),
1233 "detach" => usb_detach(args),
1234 "list" => usb_list(args),
1235 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
1236 };
1237 match result {
1238 Ok(response) => {
1239 println!("{}", response);
1240 Ok(())
1241 }
1242 Err(e) => {
1243 println!("error {}", e);
1244 Err(())
1245 }
1246 }
1247}
1248
Zach Reiznerefe95782017-08-26 18:05:48 -07001249fn print_usage() {
1250 print_help("crosvm", "[stop|run]", &[]);
1251 println!("Commands:");
1252 println!(" stop - Stops crosvm instances via their control sockets.");
1253 println!(" run - Start a new crosvm instance.");
Dylan Reid2dcb6322018-07-13 10:42:48 -07001254 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Jingkui Wang100e6e42019-03-08 20:41:57 -08001255 println!(" disk - Manage attached virtual disk devices.");
1256 println!(" usb - Manage attached virtual USB devices.");
Zach Reiznerefe95782017-08-26 18:05:48 -07001257}
1258
Dylan Reidbfba9932018-02-05 15:51:59 -08001259fn crosvm_main() -> std::result::Result<(), ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -07001260 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -08001261 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -08001262 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -07001263 }
Zach Reizner639d9672017-05-01 17:57:18 -07001264
Zach Reiznerb3fa5c92019-01-28 14:05:23 -08001265 panic_hook::set_panic_hook();
1266
Zach Reiznerefe95782017-08-26 18:05:48 -07001267 let mut args = std::env::args();
1268 if args.next().is_none() {
1269 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -08001270 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -07001271 }
Zach Reiznerefe95782017-08-26 18:05:48 -07001272
Zach Reizner8864cb02018-01-16 17:59:03 -08001273 // Past this point, usage of exit is in danger of leaking zombie processes.
Dylan Reidbfba9932018-02-05 15:51:59 -08001274 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
1275 None => {
1276 print_usage();
1277 Ok(())
1278 }
Zach Reizner55a9e502018-10-03 10:22:32 -07001279 Some("stop") => stop_vms(args),
Zach Reizner6a8fdd92019-01-16 14:38:41 -08001280 Some("suspend") => suspend_vms(args),
1281 Some("resume") => resume_vms(args),
Zach Reizner55a9e502018-10-03 10:22:32 -07001282 Some("run") => run_vm(args),
1283 Some("balloon") => balloon_vms(args),
1284 Some("create_qcow2") => create_qcow2(args),
Daniel Verkamp92f73d72018-12-04 13:17:46 -08001285 Some("disk") => disk_cmd(args),
Jingkui Wang100e6e42019-03-08 20:41:57 -08001286 Some("usb") => modify_usb(args),
Zach Reiznerefe95782017-08-26 18:05:48 -07001287 Some(c) => {
1288 println!("invalid subcommand: {:?}", c);
1289 print_usage();
Dylan Reidbfba9932018-02-05 15:51:59 -08001290 Err(())
Zach Reiznerefe95782017-08-26 18:05:48 -07001291 }
Dylan Reidbfba9932018-02-05 15:51:59 -08001292 };
Zach Reiznerefe95782017-08-26 18:05:48 -07001293
1294 // Reap exit status from any child device processes. At this point, all devices should have been
1295 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
1296 // take some time for the processes to shut down.
1297 if !wait_all_children() {
1298 // We gave them a chance, and it's too late.
1299 warn!("not all child processes have exited; sending SIGKILL");
1300 if let Err(e) = kill_process_group() {
1301 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -08001302 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -07001303 }
1304 }
1305
1306 // WARNING: Any code added after this point is not guaranteed to run
1307 // since we may forcibly kill this process (and its children) above.
Dylan Reidbfba9932018-02-05 15:51:59 -08001308 ret
1309}
1310
1311fn main() {
1312 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
Zach Reizner639d9672017-05-01 17:57:18 -07001313}
Daniel Verkamp107edb32019-04-05 09:58:48 -07001314
1315#[cfg(test)]
1316mod tests {
1317 use super::*;
1318
1319 #[test]
1320 fn parse_cpu_set_single() {
1321 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
1322 }
1323
1324 #[test]
1325 fn parse_cpu_set_list() {
1326 assert_eq!(
1327 parse_cpu_set("0,1,2,3").expect("parse failed"),
1328 vec![0, 1, 2, 3]
1329 );
1330 }
1331
1332 #[test]
1333 fn parse_cpu_set_range() {
1334 assert_eq!(
1335 parse_cpu_set("0-3").expect("parse failed"),
1336 vec![0, 1, 2, 3]
1337 );
1338 }
1339
1340 #[test]
1341 fn parse_cpu_set_list_of_ranges() {
1342 assert_eq!(
1343 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
1344 vec![3, 4, 7, 8, 9, 18]
1345 );
1346 }
1347
1348 #[test]
1349 fn parse_cpu_set_repeated() {
1350 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
1351 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
1352 }
1353
1354 #[test]
1355 fn parse_cpu_set_negative() {
1356 // Negative CPU numbers are not allowed.
1357 parse_cpu_set("-3").expect_err("parse should have failed");
1358 }
1359
1360 #[test]
1361 fn parse_cpu_set_reverse_range() {
1362 // Ranges must be from low to high.
1363 parse_cpu_set("5-2").expect_err("parse should have failed");
1364 }
1365
1366 #[test]
1367 fn parse_cpu_set_open_range() {
1368 parse_cpu_set("3-").expect_err("parse should have failed");
1369 }
1370
1371 #[test]
1372 fn parse_cpu_set_extra_comma() {
1373 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
1374 }
Trent Begin17ccaad2019-04-17 13:51:25 -06001375
1376 #[test]
1377 fn parse_serial_vaild() {
1378 parse_serial_options("type=syslog,num=1,console=true").expect("parse should have succeded");
1379 }
1380
1381 #[test]
1382 fn parse_serial_invalid_type() {
1383 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
1384 }
1385
1386 #[test]
1387 fn parse_serial_invalid_num_upper() {
1388 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
1389 }
1390
1391 #[test]
1392 fn parse_serial_invalid_num_lower() {
1393 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
1394 }
1395
1396 #[test]
1397 fn parse_serial_invalid_num_string() {
1398 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
1399 }
1400
1401 #[test]
1402 fn parse_serial_invalid_option() {
1403 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
1404 }
Daniel Verkamp107edb32019-04-05 09:58:48 -07001405}