blob: 70dbf4de0ccb2d8bad999baed0149542cc8bf369 [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
Sonny Rao2ffa0cb2018-02-26 17:27:40 -08007#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
8extern crate aarch64;
Zach Reizner55a9e502018-10-03 10:22:32 -07009extern crate arch;
Dylan Reid3082e8e2019-01-07 10:33:48 -080010extern crate audio_streams;
Zach Reizner55a9e502018-10-03 10:22:32 -070011extern crate byteorder;
Dylan Reidd169a8d2017-10-06 15:26:46 -070012extern crate devices;
Dylan Reid61edbbf2017-05-12 16:15:53 -070013extern crate io_jail;
Zach Reizner55a9e502018-10-03 10:22:32 -070014extern crate kernel_cmdline;
15extern crate kernel_loader;
Zach Reizner639d9672017-05-01 17:57:18 -070016extern crate kvm;
Zach Reizner8864cb02018-01-16 17:59:03 -080017extern crate kvm_sys;
Zach Reizner55a9e502018-10-03 10:22:32 -070018extern crate libc;
paulhsiaf052cfe2019-01-22 15:22:25 +080019extern crate libcras;
Jason D. Clinton865323d2017-09-27 22:04:03 -060020extern crate net_util;
Dylan Reid88624f82018-01-11 09:20:16 -080021extern crate qcow;
Zach Reizner55a9e502018-10-03 10:22:32 -070022#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
23extern crate x86_64;
Zach Reiznerefe95782017-08-26 18:05:48 -070024#[macro_use]
25extern crate sys_util;
Zach Reizner29ad3c72017-08-04 15:12:58 -070026extern crate data_model;
Zach Reizner55a9e502018-10-03 10:22:32 -070027#[cfg(feature = "wl-dmabuf")]
28extern crate gpu_buffer;
Jingkui Wange13b1802018-10-03 13:04:47 -070029extern crate msg_socket;
Zach Reizner8864cb02018-01-16 17:59:03 -080030#[cfg(feature = "plugin")]
31extern crate plugin_proto;
32#[cfg(feature = "plugin")]
33extern crate protobuf;
Daniel Prilik22006042019-01-14 14:19:04 -080034extern crate rand_ish;
Zach Reizner55a9e502018-10-03 10:22:32 -070035extern crate resources;
David Tolnay1d4d44a2018-12-03 23:37:46 -080036extern crate sync;
Zach Reizner55a9e502018-10-03 10:22:32 -070037extern crate vhost;
38extern crate vm_control;
Zach Reizner639d9672017-05-01 17:57:18 -070039
Zach Reiznerefe95782017-08-26 18:05:48 -070040pub mod argument;
Zach Reizner39aa26b2017-12-12 18:03:23 -080041pub mod linux;
Zach Reiznerb3fa5c92019-01-28 14:05:23 -080042pub mod panic_hook;
Zach Reizner8864cb02018-01-16 17:59:03 -080043#[cfg(feature = "plugin")]
44pub mod plugin;
Zach Reizner29ad3c72017-08-04 15:12:58 -070045
Dylan Reid2dcb6322018-07-13 10:42:48 -070046use std::fs::OpenOptions;
Stephen Barber2cfc2052017-06-21 15:16:11 -070047use std::net;
Chirantan Ekbote5f787212018-05-31 15:31:31 -070048use std::os::unix::io::RawFd;
Zach Reizner39aa26b2017-12-12 18:03:23 -080049use std::path::PathBuf;
Zach Reizner639d9672017-05-01 17:57:18 -070050use std::string::String;
Zach Reizner39aa26b2017-12-12 18:03:23 -080051use std::thread::sleep;
Stephen Barber56fbf092017-06-29 16:12:14 -070052use std::time::Duration;
Zach Reizner639d9672017-05-01 17:57:18 -070053
Dylan Reid2dcb6322018-07-13 10:42:48 -070054use qcow::QcowFile;
Zach Reiznera60744b2019-02-13 17:33:32 -080055use sys_util::{getpid, kill_process_group, net::UnixSeqpacket, reap_child, syslog};
Zach Reizner639d9672017-05-01 17:57:18 -070056
Zach Reizner55a9e502018-10-03 10:22:32 -070057use argument::{print_help, set_arguments, Argument};
Zach Reizner78986322019-02-21 20:43:21 -080058use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
59use vm_control::{VmRequest, VmResponse};
Zach Reizner639d9672017-05-01 17:57:18 -070060
Zach Reizner39aa26b2017-12-12 18:03:23 -080061static SECCOMP_POLICY_DIR: &'static str = "/usr/share/policy/crosvm";
Zach Reizneree73bf32017-08-29 16:33:28 -070062
Zach Reiznerefe95782017-08-26 18:05:48 -070063struct DiskOption {
64 path: PathBuf,
Daniel Verkampde9ae032018-08-09 16:26:59 -070065 read_only: bool,
Dylan Reidf463bc12017-07-12 19:24:57 -070066}
67
Daniel Verkampbd1a0842019-01-08 15:50:34 -080068#[allow(dead_code)]
Chirantan Ekboted41d7262018-11-16 16:37:45 -080069struct BindMount {
70 src: PathBuf,
71 dst: PathBuf,
72 writable: bool,
73}
74
Jorge E. Moreiradffec502019-01-14 18:44:49 -080075const DEFAULT_TRACKPAD_WIDTH: u32 = 800;
76const DEFAULT_TRACKPAD_HEIGHT: u32 = 1280;
77
78struct TrackpadOption {
79 path: PathBuf,
80 width: u32,
81 height: u32,
82}
83
84impl TrackpadOption {
85 fn new(path: PathBuf) -> TrackpadOption {
86 TrackpadOption {
87 path,
88 width: DEFAULT_TRACKPAD_WIDTH,
89 height: DEFAULT_TRACKPAD_HEIGHT,
90 }
91 }
92}
93
Daniel Verkampaac28132018-10-15 14:58:48 -070094pub struct Config {
95 vcpu_count: Option<u32>,
96 memory: Option<usize>,
97 kernel_path: PathBuf,
Tristan Muntsinger4133b012018-12-21 16:01:56 -080098 android_fstab: Option<PathBuf>,
Daniel Verkampe403f5c2018-12-11 16:29:26 -080099 initrd_path: Option<PathBuf>,
Daniel Verkampaac28132018-10-15 14:58:48 -0700100 params: Vec<String>,
101 socket_path: Option<PathBuf>,
102 plugin: Option<PathBuf>,
103 plugin_root: Option<PathBuf>,
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800104 plugin_mounts: Vec<BindMount>,
Zach Reiznerefe95782017-08-26 18:05:48 -0700105 disks: 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)>,
Zach Reizner639d9672017-05-01 17:57:18 -0700115 multiprocess: bool,
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700116 seccomp_policy_dir: PathBuf,
Zach Reizner3a8100a2017-09-13 19:15:43 -0700117 gpu: bool,
David Tolnay43f8e212019-02-13 17:28:16 -0800118 software_tpm: bool,
paulhsiaf052cfe2019-01-22 15:22:25 +0800119 cras_audio: bool,
Dylan Reid3082e8e2019-01-07 10:33:48 -0800120 null_audio: bool,
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800121 virtio_trackpad: Option<TrackpadOption>,
122 virtio_mouse: Option<PathBuf>,
123 virtio_keyboard: Option<PathBuf>,
124 virtio_input_evdevs: Vec<PathBuf>,
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800125 split_irqchip: bool,
Dylan Reid059a1882018-07-23 17:58:09 -0700126}
127
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700128impl Default for Config {
129 fn default() -> Config {
130 Config {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700131 vcpu_count: None,
132 memory: None,
133 kernel_path: PathBuf::default(),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800134 android_fstab: None,
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800135 initrd_path: None,
Zach Reiznerbb678712018-01-30 18:13:04 -0800136 params: Vec::new(),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700137 socket_path: None,
Zach Reizner8864cb02018-01-16 17:59:03 -0800138 plugin: None,
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700139 plugin_root: None,
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800140 plugin_mounts: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700141 disks: Vec::new(),
142 host_ip: None,
143 netmask: None,
144 mac_address: None,
145 vhost_net: false,
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800146 tap_fd: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700147 cid: None,
148 gpu: false,
David Tolnay43f8e212019-02-13 17:28:16 -0800149 software_tpm: false,
Daniel Verkampaac28132018-10-15 14:58:48 -0700150 wayland_socket_path: None,
151 wayland_dmabuf: false,
152 shared_dirs: Vec::new(),
153 multiprocess: !cfg!(feature = "default-no-sandbox"),
154 seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
paulhsiaf052cfe2019-01-22 15:22:25 +0800155 cras_audio: false,
Dylan Reid3082e8e2019-01-07 10:33:48 -0800156 null_audio: false,
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800157 virtio_trackpad: None,
158 virtio_mouse: None,
159 virtio_keyboard: None,
160 virtio_input_evdevs: Vec::new(),
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800161 split_irqchip: false,
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700162 }
163 }
164}
165
Stephen Barbera00753b2017-07-18 13:57:26 -0700166// Wait for all children to exit. Return true if they have all exited, false
167// otherwise.
168fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -0700169 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -0700170 const CHILD_WAIT_MS: u64 = 10;
171 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -0700172 loop {
Zach Reizner56158c82017-08-24 13:50:14 -0700173 match reap_child() {
174 Ok(0) => break,
175 // We expect ECHILD which indicates that there were no children left.
176 Err(e) if e.errno() == libc::ECHILD => return true,
177 Err(e) => {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800178 warn!("error while waiting for children: {}", e);
Zach Reizner56158c82017-08-24 13:50:14 -0700179 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -0700180 }
Zach Reizner56158c82017-08-24 13:50:14 -0700181 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -0700182 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -0700183 }
184 }
Zach Reizner56158c82017-08-24 13:50:14 -0700185 // There's no timeout option for waitpid which reap_child calls internally, so our only
186 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -0700187 sleep(Duration::from_millis(CHILD_WAIT_MS));
188 }
189
190 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -0800191 false
Stephen Barbera00753b2017-07-18 13:57:26 -0700192}
193
Zach Reiznerefe95782017-08-26 18:05:48 -0700194fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
195 match name {
196 "" => {
Zach Reizner8864cb02018-01-16 17:59:03 -0800197 if cfg.plugin.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700198 return Err(argument::Error::TooManyArguments(
199 "`plugin` can not be used with kernel".to_owned(),
200 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800201 } else if !cfg.kernel_path.as_os_str().is_empty() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700202 return Err(argument::Error::TooManyArguments(
203 "expected exactly one kernel path".to_owned(),
204 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700205 } else {
206 let kernel_path = PathBuf::from(value.unwrap());
207 if !kernel_path.exists() {
208 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700209 value: value.unwrap().to_owned(),
210 expected: "this kernel path does not exist",
211 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700212 }
213 cfg.kernel_path = kernel_path;
214 }
215 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800216 "android-fstab" => {
217 if cfg.android_fstab.is_some()
218 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
219 {
220 return Err(argument::Error::TooManyArguments(
221 "expected exactly one android fstab path".to_owned(),
222 ));
223 } else {
224 let android_fstab = PathBuf::from(value.unwrap());
225 if !android_fstab.exists() {
226 return Err(argument::Error::InvalidValue {
227 value: value.unwrap().to_owned(),
228 expected: "this android fstab path does not exist",
229 });
230 }
231 cfg.android_fstab = Some(android_fstab);
232 }
233 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700234 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -0800235 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -0700236 }
237 "cpus" => {
238 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700239 return Err(argument::Error::TooManyArguments(
240 "`cpus` already given".to_owned(),
241 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700242 }
243 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -0700244 Some(
245 value
246 .unwrap()
247 .parse()
248 .map_err(|_| argument::Error::InvalidValue {
249 value: value.unwrap().to_owned(),
250 expected: "this value for `cpus` needs to be integer",
251 })?,
252 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700253 }
254 "mem" => {
255 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700256 return Err(argument::Error::TooManyArguments(
257 "`mem` already given".to_owned(),
258 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700259 }
260 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -0700261 Some(
262 value
263 .unwrap()
264 .parse()
265 .map_err(|_| argument::Error::InvalidValue {
266 value: value.unwrap().to_owned(),
267 expected: "this value for `mem` needs to be integer",
268 })?,
269 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700270 }
paulhsiaf052cfe2019-01-22 15:22:25 +0800271 "cras-audio" => {
272 cfg.cras_audio = true;
273 }
Dylan Reid3082e8e2019-01-07 10:33:48 -0800274 "null-audio" => {
275 cfg.null_audio = true;
276 }
Dylan Reid88624f82018-01-11 09:20:16 -0800277 "root" | "disk" | "rwdisk" | "qcow" | "rwqcow" => {
Zach Reiznerefe95782017-08-26 18:05:48 -0700278 let disk_path = PathBuf::from(value.unwrap());
279 if !disk_path.exists() {
280 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700281 value: value.unwrap().to_owned(),
282 expected: "this disk path does not exist",
283 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700284 }
285 if name == "root" {
Daniel Verkampaac28132018-10-15 14:58:48 -0700286 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -0700287 return Err(argument::Error::TooManyArguments(
288 "ran out of letters for to assign to root disk".to_owned(),
289 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700290 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700291 cfg.params.push(format!(
292 "root=/dev/vd{} ro",
David Tolnay5bbbf612018-12-01 17:49:30 -0800293 char::from(b'a' + cfg.disks.len() as u8)
Zach Reizner55a9e502018-10-03 10:22:32 -0700294 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700295 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700296 cfg.disks.push(DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -0700297 path: disk_path,
298 read_only: !name.starts_with("rw"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700299 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700300 }
301 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700302 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700303 return Err(argument::Error::TooManyArguments(
304 "`host_ip` already given".to_owned(),
305 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700306 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700307 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -0700308 Some(
309 value
310 .unwrap()
311 .parse()
312 .map_err(|_| argument::Error::InvalidValue {
313 value: value.unwrap().to_owned(),
314 expected: "`host_ip` needs to be in the form \"x.x.x.x\"",
315 })?,
316 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700317 }
318 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700319 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700320 return Err(argument::Error::TooManyArguments(
321 "`netmask` already given".to_owned(),
322 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700323 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700324 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -0700325 Some(
326 value
327 .unwrap()
328 .parse()
329 .map_err(|_| argument::Error::InvalidValue {
330 value: value.unwrap().to_owned(),
331 expected: "`netmask` needs to be in the form \"x.x.x.x\"",
332 })?,
333 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700334 }
335 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700336 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700337 return Err(argument::Error::TooManyArguments(
338 "`mac` already given".to_owned(),
339 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700340 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700341 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -0700342 Some(
343 value
344 .unwrap()
345 .parse()
346 .map_err(|_| argument::Error::InvalidValue {
347 value: value.unwrap().to_owned(),
348 expected: "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
349 })?,
350 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700351 }
Stephen Barber28a5a612017-10-20 17:15:30 -0700352 "wayland-sock" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700353 if cfg.wayland_socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700354 return Err(argument::Error::TooManyArguments(
355 "`wayland-sock` already given".to_owned(),
356 ));
Stephen Barber28a5a612017-10-20 17:15:30 -0700357 }
358 let wayland_socket_path = PathBuf::from(value.unwrap());
359 if !wayland_socket_path.exists() {
360 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700361 value: value.unwrap().to_string(),
362 expected: "Wayland socket does not exist",
363 });
Stephen Barber28a5a612017-10-20 17:15:30 -0700364 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700365 cfg.wayland_socket_path = Some(wayland_socket_path);
Stephen Barber28a5a612017-10-20 17:15:30 -0700366 }
David Reveman52ba4e52018-04-22 21:42:09 -0400367 #[cfg(feature = "wl-dmabuf")]
Daniel Verkampaac28132018-10-15 14:58:48 -0700368 "wayland-dmabuf" => cfg.wayland_dmabuf = true,
Zach Reiznerefe95782017-08-26 18:05:48 -0700369 "socket" => {
370 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700371 return Err(argument::Error::TooManyArguments(
372 "`socket` already given".to_owned(),
373 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700374 }
375 let mut socket_path = PathBuf::from(value.unwrap());
376 if socket_path.is_dir() {
377 socket_path.push(format!("crosvm-{}.sock", getpid()));
378 }
379 if socket_path.exists() {
380 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700381 value: socket_path.to_string_lossy().into_owned(),
382 expected: "this socket path already exists",
383 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700384 }
385 cfg.socket_path = Some(socket_path);
386 }
387 "multiprocess" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700388 cfg.multiprocess = true;
Zach Reiznerefe95782017-08-26 18:05:48 -0700389 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700390 "disable-sandbox" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700391 cfg.multiprocess = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700392 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700393 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700394 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700395 return Err(argument::Error::TooManyArguments(
396 "`cid` alread given".to_owned(),
397 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700398 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700399 cfg.cid = Some(
400 value
401 .unwrap()
402 .parse()
403 .map_err(|_| argument::Error::InvalidValue {
404 value: value.unwrap().to_owned(),
405 expected: "this value for `cid` must be an unsigned integer",
406 })?,
407 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700408 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700409 "shared-dir" => {
410 // Formatted as <src:tag>.
411 let param = value.unwrap();
412 let mut components = param.splitn(2, ':');
Zach Reizner55a9e502018-10-03 10:22:32 -0700413 let src =
414 PathBuf::from(
415 components
416 .next()
417 .ok_or_else(|| argument::Error::InvalidValue {
418 value: param.to_owned(),
419 expected: "missing source path for `shared-dir`",
420 })?,
421 );
422 let tag = components
423 .next()
424 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700425 value: param.to_owned(),
426 expected: "missing tag for `shared-dir`",
David Tolnay2bac1e72018-12-12 14:33:42 -0800427 })?
428 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700429
430 if !src.is_dir() {
431 return Err(argument::Error::InvalidValue {
432 value: param.to_owned(),
433 expected: "source path for `shared-dir` must be a directory",
434 });
435 }
436
Daniel Verkampaac28132018-10-15 14:58:48 -0700437 cfg.shared_dirs.push((src, tag));
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700438 }
Dylan Reide026ef02017-10-02 19:03:52 -0700439 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700440 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -0700441 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -0700442 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800443 "plugin" => {
444 if !cfg.kernel_path.as_os_str().is_empty() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700445 return Err(argument::Error::TooManyArguments(
446 "`plugin` can not be used with kernel".to_owned(),
447 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800448 } else if cfg.plugin.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700449 return Err(argument::Error::TooManyArguments(
450 "`plugin` already given".to_owned(),
451 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800452 }
Zach Reiznercc30d582018-01-23 21:16:42 -0800453 let plugin = PathBuf::from(value.unwrap().to_owned());
454 if plugin.is_relative() {
455 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700456 value: plugin.to_string_lossy().into_owned(),
457 expected: "the plugin path must be an absolute path",
458 });
Zach Reiznercc30d582018-01-23 21:16:42 -0800459 }
460 cfg.plugin = Some(plugin);
Zach Reizner55a9e502018-10-03 10:22:32 -0700461 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700462 "plugin-root" => {
463 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -0700464 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800465 "plugin-mount" => {
466 let components: Vec<&str> = value.unwrap().split(":").collect();
467 if components.len() != 3 {
468 return Err(argument::Error::InvalidValue {
469 value: value.unwrap().to_owned(),
470 expected:
471 "`plugin-mount` must have exactly 3 components: <src>:<dst>:<writable>",
472 });
473 }
474
475 let src = PathBuf::from(components[0]);
476 if src.is_relative() {
477 return Err(argument::Error::InvalidValue {
478 value: components[0].to_owned(),
479 expected: "the source path for `plugin-mount` must be absolute",
480 });
481 }
482 if !src.exists() {
483 return Err(argument::Error::InvalidValue {
484 value: components[0].to_owned(),
485 expected: "the source path for `plugin-mount` does not exist",
486 });
487 }
488
489 let dst = PathBuf::from(components[1]);
490 if dst.is_relative() {
491 return Err(argument::Error::InvalidValue {
492 value: components[1].to_owned(),
493 expected: "the destination path for `plugin-mount` must be absolute",
494 });
495 }
496
497 let writable: bool =
498 components[2]
499 .parse()
500 .map_err(|_| argument::Error::InvalidValue {
501 value: components[2].to_owned(),
502 expected: "the <writable> component for `plugin-mount` is not valid bool",
503 })?;
504
505 cfg.plugin_mounts.push(BindMount { src, dst, writable });
506 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700507 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700508 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800509 cfg.tap_fd.push(
510 value
511 .unwrap()
512 .parse()
513 .map_err(|_| argument::Error::InvalidValue {
514 value: value.unwrap().to_owned(),
515 expected: "this value for `tap-fd` must be an unsigned integer",
516 })?,
517 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700518 }
Zach Reizner3a8100a2017-09-13 19:15:43 -0700519 "gpu" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700520 cfg.gpu = true;
Zach Reizner3a8100a2017-09-13 19:15:43 -0700521 }
David Tolnay43f8e212019-02-13 17:28:16 -0800522 "software-tpm" => {
523 cfg.software_tpm = true;
524 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800525 "trackpad" => {
526 if cfg.virtio_trackpad.is_some() {
527 return Err(argument::Error::TooManyArguments(
528 "`trackpad` already given".to_owned(),
529 ));
530 }
531 let mut it = value.unwrap().split(":");
532
533 let mut trackpad_spec =
534 TrackpadOption::new(PathBuf::from(it.next().unwrap().to_owned()));
535 if let Some(width) = it.next() {
536 trackpad_spec.width = width.trim().parse().unwrap();
537 }
538 if let Some(height) = it.next() {
539 trackpad_spec.height = height.trim().parse().unwrap();
540 }
541
542 cfg.virtio_trackpad = Some(trackpad_spec);
543 }
544 "mouse" => {
545 if cfg.virtio_mouse.is_some() {
546 return Err(argument::Error::TooManyArguments(
547 "`mouse` already given".to_owned(),
548 ));
549 }
550 cfg.virtio_mouse = Some(PathBuf::from(value.unwrap().to_owned()));
551 }
552 "keyboard" => {
553 if cfg.virtio_keyboard.is_some() {
554 return Err(argument::Error::TooManyArguments(
555 "`keyboard` already given".to_owned(),
556 ));
557 }
558 cfg.virtio_keyboard = Some(PathBuf::from(value.unwrap().to_owned()));
559 }
560 "evdev" => {
561 let dev_path = PathBuf::from(value.unwrap());
562 if !dev_path.exists() {
563 return Err(argument::Error::InvalidValue {
564 value: value.unwrap().to_owned(),
565 expected: "this input device path does not exist",
566 });
567 }
568 cfg.virtio_input_evdevs.push(dev_path);
569 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800570 "split-irqchip" => {
571 cfg.split_irqchip = true;
572 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800573 "initrd" => {
574 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
575 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700576 "help" => return Err(argument::Error::PrintHelp),
577 _ => unreachable!(),
578 }
579 Ok(())
580}
581
Dylan Reidbfba9932018-02-05 15:51:59 -0800582fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -0700583 let arguments =
584 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800585 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800586 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700587 Argument::short_value('p',
588 "params",
589 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -0800590 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700591 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
592 Argument::short_value('m',
593 "mem",
594 "N",
595 "Amount of guest memory in MiB. (default: 256)"),
596 Argument::short_value('r',
597 "root",
598 "PATH",
599 "Path to a root disk image. Like `--disk` but adds appropriate kernel command line option."),
600 Argument::short_value('d', "disk", "PATH", "Path to a disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700601 Argument::value("qcow", "PATH", "Path to a qcow2 disk image. (Deprecated; use --disk instead.)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700602 Argument::value("rwdisk", "PATH", "Path to a writable disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700603 Argument::value("rwqcow", "PATH", "Path to a writable qcow2 disk image. (Deprecated; use --rwdisk instead.)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700604 Argument::value("host_ip",
605 "IP",
606 "IP address to assign to host tap interface."),
607 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
608 Argument::value("mac", "MAC", "MAC address for VM."),
paulhsiaf052cfe2019-01-22 15:22:25 +0800609 Argument::flag("cras-audio", "Add an audio device to the VM that plays samples through CRAS server"),
Dylan Reid3082e8e2019-01-07 10:33:48 -0800610 Argument::flag("null-audio", "Add an audio device to the VM that plays samples to /dev/null"),
Stephen Barber28a5a612017-10-20 17:15:30 -0700611 Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
David Reveman52ba4e52018-04-22 21:42:09 -0400612 #[cfg(feature = "wl-dmabuf")]
613 Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700614 Argument::short_value('s',
615 "socket",
616 "PATH",
617 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700618 Argument::short_flag('u', "multiprocess", "Run each device in a child process(default)."),
619 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700620 Argument::value("cid", "CID", "Context ID for virtual sockets."),
621 Argument::value("shared-dir", "PATH:TAG",
622 "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 -0700623 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner8864cb02018-01-16 17:59:03 -0800624 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -0800625 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -0800626 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700627 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 -0800628 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800629 Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem. Can be given more than once."),
Rob Bradford8f002f52018-02-19 16:31:11 +0000630 Argument::flag("vhost-net", "Use vhost for networking."),
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700631 Argument::value("tap-fd",
632 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800633 "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 -0700634 #[cfg(feature = "gpu")]
635 Argument::flag("gpu", "(EXPERIMENTAL) enable virtio-gpu device"),
David Tolnay43f8e212019-02-13 17:28:16 -0800636 #[cfg(feature = "tpm")]
637 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800638 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"),
639 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)."),
640 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
641 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 -0800642 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
643 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700644 Argument::short_flag('h', "help", "Print help message.")];
645
646 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -0700647 let match_res = set_arguments(args, &arguments[..], |name, value| {
648 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -0800649 })
650 .and_then(|_| {
Zach Reizner8864cb02018-01-16 17:59:03 -0800651 if cfg.kernel_path.as_os_str().is_empty() && cfg.plugin.is_none() {
Zach Reiznerefe95782017-08-26 18:05:48 -0700652 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
653 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700654 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
655 if cfg.host_ip.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700656 return Err(argument::Error::ExpectedArgument(
657 "`host_ip` missing from network config".to_owned(),
658 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700659 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700660 if cfg.netmask.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700661 return Err(argument::Error::ExpectedArgument(
662 "`netmask` missing from network config".to_owned(),
663 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700664 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700665 if cfg.mac_address.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700666 return Err(argument::Error::ExpectedArgument(
667 "`mac` missing from network config".to_owned(),
668 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700669 }
670 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700671 if cfg.plugin_root.is_some() && cfg.plugin.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700672 return Err(argument::Error::ExpectedArgument(
673 "`plugin-root` requires `plugin`".to_owned(),
674 ));
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700675 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700676 Ok(())
677 });
678
679 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -0800680 #[cfg(feature = "plugin")]
David Tolnay2bac1e72018-12-12 14:33:42 -0800681 Ok(()) if cfg.plugin.is_some() => match plugin::run_config(cfg) {
682 Ok(_) => {
683 info!("crosvm and plugin have exited normally");
684 Ok(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800685 }
David Tolnay2bac1e72018-12-12 14:33:42 -0800686 Err(e) => {
687 error!("{}", e);
688 Err(())
689 }
690 },
Zach Reizner55a9e502018-10-03 10:22:32 -0700691 Ok(()) => match linux::run_config(cfg) {
692 Ok(_) => {
693 info!("crosvm has exited normally");
694 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -0700695 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700696 Err(e) => {
697 error!("{}", e);
698 Err(())
699 }
700 },
Dylan Reidbfba9932018-02-05 15:51:59 -0800701 Err(argument::Error::PrintHelp) => {
702 print_help("crosvm run", "KERNEL", &arguments[..]);
703 Ok(())
704 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800705 Err(e) => {
706 println!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -0800707 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800708 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700709 }
710}
711
Zach Reizner78986322019-02-21 20:43:21 -0800712fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()> {
Dylan Reidbfba9932018-02-05 15:51:59 -0800713 let mut return_result = Ok(());
Zach Reiznerefe95782017-08-26 18:05:48 -0700714 for socket_path in args {
Zach Reiznera60744b2019-02-13 17:33:32 -0800715 match UnixSeqpacket::connect(&socket_path) {
Zach Reiznerefe95782017-08-26 18:05:48 -0700716 Ok(s) => {
Zach Reizner78986322019-02-21 20:43:21 -0800717 let socket = MsgSocket::<VmRequest, VmResponse>::new(s);
718 if let Err(e) = socket.send(request) {
Zach Reizner55a9e502018-10-03 10:22:32 -0700719 error!(
David Tolnayb4bd00f2019-02-12 17:51:26 -0800720 "failed to send request to socket at '{}': {}",
Zach Reizner55a9e502018-10-03 10:22:32 -0700721 socket_path, e
722 );
Zach Reizner78986322019-02-21 20:43:21 -0800723 return_result = Err(());
724 continue;
Zach Reiznerefe95782017-08-26 18:05:48 -0700725 }
Zach Reizner78986322019-02-21 20:43:21 -0800726 match socket.recv() {
727 Ok(response) => info!("request response was {}", response),
728 Err(e) => {
729 error!(
730 "failed to send request to socket at2 '{}': {}",
731 socket_path, e
732 );
733 return_result = Err(());
734 continue;
735 }
Dylan Reidd4432042017-12-06 18:20:09 -0800736 }
737 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800738 Err(e) => {
739 error!("failed to connect to socket at '{}': {}", socket_path, e);
740 return_result = Err(());
741 }
Dylan Reidd4432042017-12-06 18:20:09 -0800742 }
743 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800744
745 return_result
Dylan Reidd4432042017-12-06 18:20:09 -0800746}
Zach Reiznerefe95782017-08-26 18:05:48 -0700747
Zach Reizner78986322019-02-21 20:43:21 -0800748fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> {
749 if args.len() == 0 {
750 print_help("crosvm stop", "VM_SOCKET...", &[]);
751 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
752 return Ok(());
753 }
754 vms_request(&VmRequest::Exit, args)
755}
756
757fn suspend_vms(args: std::env::Args) -> std::result::Result<(), ()> {
758 if args.len() == 0 {
759 print_help("crosvm suspend", "VM_SOCKET...", &[]);
760 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
761 return Ok(());
762 }
763 vms_request(&VmRequest::Suspend, args)
764}
765
766fn resume_vms(args: std::env::Args) -> std::result::Result<(), ()> {
767 if args.len() == 0 {
768 print_help("crosvm resume", "VM_SOCKET...", &[]);
769 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
770 return Ok(());
771 }
772 vms_request(&VmRequest::Resume, args)
773}
774
775fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
776 if args.len() < 2 {
777 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
778 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
779 return Ok(());
780 }
781 let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
782 Ok(n) => n,
783 Err(_) => {
784 error!("Failed to parse number of bytes");
785 return Err(());
786 }
787 };
788
789 vms_request(&VmRequest::BalloonAdjust(num_bytes), args)
790}
791
Dylan Reid2dcb6322018-07-13 10:42:48 -0700792fn create_qcow2(mut args: std::env::Args) -> std::result::Result<(), ()> {
793 if args.len() != 2 {
794 print_help("crosvm create_qcow2", "PATH SIZE", &[]);
Dylan Reid940259c2018-07-20 14:22:33 -0700795 println!("Create a new QCOW2 image at `PATH` of the specified `SIZE` in bytes.");
Dylan Reid2dcb6322018-07-13 10:42:48 -0700796 }
797 let file_path = args.nth(0).unwrap();
798 let size: u64 = match args.nth(0).unwrap().parse::<u64>() {
799 Ok(n) => n,
800 Err(_) => {
801 error!("Failed to parse size of the disk.");
802 return Err(());
Zach Reizner55a9e502018-10-03 10:22:32 -0700803 }
Dylan Reid2dcb6322018-07-13 10:42:48 -0700804 };
805
806 let file = OpenOptions::new()
807 .create(true)
808 .read(true)
809 .write(true)
810 .open(&file_path)
811 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800812 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -0700813 })?;
814
Zach Reizner55a9e502018-10-03 10:22:32 -0700815 QcowFile::new(file, size).map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800816 error!("Failed to create qcow file at '{}': {}", file_path, e);
Zach Reizner55a9e502018-10-03 10:22:32 -0700817 })?;
Dylan Reid2dcb6322018-07-13 10:42:48 -0700818
819 Ok(())
820}
821
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800822fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
823 if args.len() < 2 {
824 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
825 println!("Manage attached virtual disk devices.");
826 println!("Subcommands:");
827 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Zach Reizner78986322019-02-21 20:43:21 -0800828 return Ok(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800829 }
830 let subcommand: &str = &args.nth(0).unwrap();
831
832 let request = match subcommand {
833 "resize" => {
834 let disk_index = match args.nth(0).unwrap().parse::<usize>() {
835 Ok(n) => n,
836 Err(_) => {
837 error!("Failed to parse disk index");
838 return Err(());
839 }
840 };
841
842 let new_size = match args.nth(0).unwrap().parse::<u64>() {
843 Ok(n) => n,
844 Err(_) => {
845 error!("Failed to parse disk size");
846 return Err(());
847 }
848 };
849
850 VmRequest::DiskResize {
851 disk_index,
852 new_size,
853 }
854 }
855 _ => {
856 error!("Unknown disk subcommand '{}'", subcommand);
857 return Err(());
858 }
859 };
860
Zach Reizner78986322019-02-21 20:43:21 -0800861 vms_request(&request, args)
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800862}
863
Zach Reiznerefe95782017-08-26 18:05:48 -0700864fn print_usage() {
865 print_help("crosvm", "[stop|run]", &[]);
866 println!("Commands:");
867 println!(" stop - Stops crosvm instances via their control sockets.");
868 println!(" run - Start a new crosvm instance.");
Dylan Reid2dcb6322018-07-13 10:42:48 -0700869 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800870 println!(" disk - Manage attached virtual disk devices.")
Zach Reiznerefe95782017-08-26 18:05:48 -0700871}
872
Dylan Reidbfba9932018-02-05 15:51:59 -0800873fn crosvm_main() -> std::result::Result<(), ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -0700874 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800875 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -0800876 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -0700877 }
Zach Reizner639d9672017-05-01 17:57:18 -0700878
Zach Reiznerb3fa5c92019-01-28 14:05:23 -0800879 panic_hook::set_panic_hook();
880
Zach Reiznerefe95782017-08-26 18:05:48 -0700881 let mut args = std::env::args();
882 if args.next().is_none() {
883 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -0800884 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -0700885 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700886
Zach Reizner8864cb02018-01-16 17:59:03 -0800887 // Past this point, usage of exit is in danger of leaking zombie processes.
Dylan Reidbfba9932018-02-05 15:51:59 -0800888 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
889 None => {
890 print_usage();
891 Ok(())
892 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700893 Some("stop") => stop_vms(args),
Zach Reizner6a8fdd92019-01-16 14:38:41 -0800894 Some("suspend") => suspend_vms(args),
895 Some("resume") => resume_vms(args),
Zach Reizner55a9e502018-10-03 10:22:32 -0700896 Some("run") => run_vm(args),
897 Some("balloon") => balloon_vms(args),
898 Some("create_qcow2") => create_qcow2(args),
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800899 Some("disk") => disk_cmd(args),
Zach Reiznerefe95782017-08-26 18:05:48 -0700900 Some(c) => {
901 println!("invalid subcommand: {:?}", c);
902 print_usage();
Dylan Reidbfba9932018-02-05 15:51:59 -0800903 Err(())
Zach Reiznerefe95782017-08-26 18:05:48 -0700904 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800905 };
Zach Reiznerefe95782017-08-26 18:05:48 -0700906
907 // Reap exit status from any child device processes. At this point, all devices should have been
908 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
909 // take some time for the processes to shut down.
910 if !wait_all_children() {
911 // We gave them a chance, and it's too late.
912 warn!("not all child processes have exited; sending SIGKILL");
913 if let Err(e) = kill_process_group() {
914 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -0800915 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -0700916 }
917 }
918
919 // WARNING: Any code added after this point is not guaranteed to run
920 // since we may forcibly kill this process (and its children) above.
Dylan Reidbfba9932018-02-05 15:51:59 -0800921 ret
922}
923
924fn main() {
925 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
Zach Reizner639d9672017-05-01 17:57:18 -0700926}