blob: c2e294f320c6e3af4940b13217d9a0f7f533cf6e [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
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -080075#[allow(dead_code)]
76struct GidMap {
77 inner: libc::gid_t,
78 outer: libc::gid_t,
79 count: u32,
80}
81
Jorge E. Moreiradffec502019-01-14 18:44:49 -080082const DEFAULT_TRACKPAD_WIDTH: u32 = 800;
83const DEFAULT_TRACKPAD_HEIGHT: u32 = 1280;
84
85struct TrackpadOption {
86 path: PathBuf,
87 width: u32,
88 height: u32,
89}
90
91impl TrackpadOption {
92 fn new(path: PathBuf) -> TrackpadOption {
93 TrackpadOption {
94 path,
95 width: DEFAULT_TRACKPAD_WIDTH,
96 height: DEFAULT_TRACKPAD_HEIGHT,
97 }
98 }
99}
100
Daniel Verkampaac28132018-10-15 14:58:48 -0700101pub struct Config {
102 vcpu_count: Option<u32>,
103 memory: Option<usize>,
104 kernel_path: PathBuf,
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800105 android_fstab: Option<PathBuf>,
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800106 initrd_path: Option<PathBuf>,
Daniel Verkampaac28132018-10-15 14:58:48 -0700107 params: Vec<String>,
108 socket_path: Option<PathBuf>,
109 plugin: Option<PathBuf>,
110 plugin_root: Option<PathBuf>,
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800111 plugin_mounts: Vec<BindMount>,
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -0800112 plugin_gid_maps: Vec<GidMap>,
Zach Reiznerefe95782017-08-26 18:05:48 -0700113 disks: Vec<DiskOption>,
Stephen Barber2cfc2052017-06-21 15:16:11 -0700114 host_ip: Option<net::Ipv4Addr>,
115 netmask: Option<net::Ipv4Addr>,
Stephen Barber308ff602018-02-13 22:47:07 -0800116 mac_address: Option<net_util::MacAddress>,
Stephen Barber5e77e882017-08-07 17:13:38 -0700117 vhost_net: bool,
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800118 tap_fd: Vec<RawFd>,
Dylan Reid059a1882018-07-23 17:58:09 -0700119 cid: Option<u64>,
Stephen Barber28a5a612017-10-20 17:15:30 -0700120 wayland_socket_path: Option<PathBuf>,
David Reveman52ba4e52018-04-22 21:42:09 -0400121 wayland_dmabuf: bool,
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700122 shared_dirs: Vec<(PathBuf, String)>,
Zach Reizner639d9672017-05-01 17:57:18 -0700123 multiprocess: bool,
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700124 seccomp_policy_dir: PathBuf,
Zach Reizner3a8100a2017-09-13 19:15:43 -0700125 gpu: bool,
David Tolnay43f8e212019-02-13 17:28:16 -0800126 software_tpm: bool,
paulhsiaf052cfe2019-01-22 15:22:25 +0800127 cras_audio: bool,
Dylan Reid3082e8e2019-01-07 10:33:48 -0800128 null_audio: bool,
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800129 virtio_trackpad: Option<TrackpadOption>,
130 virtio_mouse: Option<PathBuf>,
131 virtio_keyboard: Option<PathBuf>,
132 virtio_input_evdevs: Vec<PathBuf>,
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800133 split_irqchip: bool,
Dylan Reid059a1882018-07-23 17:58:09 -0700134}
135
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700136impl Default for Config {
137 fn default() -> Config {
138 Config {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700139 vcpu_count: None,
140 memory: None,
141 kernel_path: PathBuf::default(),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800142 android_fstab: None,
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800143 initrd_path: None,
Zach Reiznerbb678712018-01-30 18:13:04 -0800144 params: Vec::new(),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700145 socket_path: None,
Zach Reizner8864cb02018-01-16 17:59:03 -0800146 plugin: None,
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700147 plugin_root: None,
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800148 plugin_mounts: Vec::new(),
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -0800149 plugin_gid_maps: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700150 disks: Vec::new(),
151 host_ip: None,
152 netmask: None,
153 mac_address: None,
154 vhost_net: false,
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800155 tap_fd: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700156 cid: None,
157 gpu: false,
David Tolnay43f8e212019-02-13 17:28:16 -0800158 software_tpm: false,
Daniel Verkampaac28132018-10-15 14:58:48 -0700159 wayland_socket_path: None,
160 wayland_dmabuf: false,
161 shared_dirs: Vec::new(),
162 multiprocess: !cfg!(feature = "default-no-sandbox"),
163 seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
paulhsiaf052cfe2019-01-22 15:22:25 +0800164 cras_audio: false,
Dylan Reid3082e8e2019-01-07 10:33:48 -0800165 null_audio: false,
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800166 virtio_trackpad: None,
167 virtio_mouse: None,
168 virtio_keyboard: None,
169 virtio_input_evdevs: Vec::new(),
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800170 split_irqchip: false,
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700171 }
172 }
173}
174
Stephen Barbera00753b2017-07-18 13:57:26 -0700175// Wait for all children to exit. Return true if they have all exited, false
176// otherwise.
177fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -0700178 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -0700179 const CHILD_WAIT_MS: u64 = 10;
180 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -0700181 loop {
Zach Reizner56158c82017-08-24 13:50:14 -0700182 match reap_child() {
183 Ok(0) => break,
184 // We expect ECHILD which indicates that there were no children left.
185 Err(e) if e.errno() == libc::ECHILD => return true,
186 Err(e) => {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800187 warn!("error while waiting for children: {}", e);
Zach Reizner56158c82017-08-24 13:50:14 -0700188 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -0700189 }
Zach Reizner56158c82017-08-24 13:50:14 -0700190 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -0700191 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -0700192 }
193 }
Zach Reizner56158c82017-08-24 13:50:14 -0700194 // There's no timeout option for waitpid which reap_child calls internally, so our only
195 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -0700196 sleep(Duration::from_millis(CHILD_WAIT_MS));
197 }
198
199 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -0800200 false
Stephen Barbera00753b2017-07-18 13:57:26 -0700201}
202
Zach Reiznerefe95782017-08-26 18:05:48 -0700203fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
204 match name {
205 "" => {
Zach Reizner8864cb02018-01-16 17:59:03 -0800206 if cfg.plugin.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700207 return Err(argument::Error::TooManyArguments(
208 "`plugin` can not be used with kernel".to_owned(),
209 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800210 } else if !cfg.kernel_path.as_os_str().is_empty() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700211 return Err(argument::Error::TooManyArguments(
212 "expected exactly one kernel path".to_owned(),
213 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700214 } else {
215 let kernel_path = PathBuf::from(value.unwrap());
216 if !kernel_path.exists() {
217 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700218 value: value.unwrap().to_owned(),
219 expected: "this kernel path does not exist",
220 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700221 }
222 cfg.kernel_path = kernel_path;
223 }
224 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800225 "android-fstab" => {
226 if cfg.android_fstab.is_some()
227 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
228 {
229 return Err(argument::Error::TooManyArguments(
230 "expected exactly one android fstab path".to_owned(),
231 ));
232 } else {
233 let android_fstab = PathBuf::from(value.unwrap());
234 if !android_fstab.exists() {
235 return Err(argument::Error::InvalidValue {
236 value: value.unwrap().to_owned(),
237 expected: "this android fstab path does not exist",
238 });
239 }
240 cfg.android_fstab = Some(android_fstab);
241 }
242 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700243 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -0800244 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -0700245 }
246 "cpus" => {
247 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700248 return Err(argument::Error::TooManyArguments(
249 "`cpus` already given".to_owned(),
250 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700251 }
252 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -0700253 Some(
254 value
255 .unwrap()
256 .parse()
257 .map_err(|_| argument::Error::InvalidValue {
258 value: value.unwrap().to_owned(),
259 expected: "this value for `cpus` needs to be integer",
260 })?,
261 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700262 }
263 "mem" => {
264 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700265 return Err(argument::Error::TooManyArguments(
266 "`mem` already given".to_owned(),
267 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700268 }
269 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -0700270 Some(
271 value
272 .unwrap()
273 .parse()
274 .map_err(|_| argument::Error::InvalidValue {
275 value: value.unwrap().to_owned(),
276 expected: "this value for `mem` needs to be integer",
277 })?,
278 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700279 }
paulhsiaf052cfe2019-01-22 15:22:25 +0800280 "cras-audio" => {
281 cfg.cras_audio = true;
282 }
Dylan Reid3082e8e2019-01-07 10:33:48 -0800283 "null-audio" => {
284 cfg.null_audio = true;
285 }
Dylan Reid88624f82018-01-11 09:20:16 -0800286 "root" | "disk" | "rwdisk" | "qcow" | "rwqcow" => {
Zach Reiznerefe95782017-08-26 18:05:48 -0700287 let disk_path = PathBuf::from(value.unwrap());
288 if !disk_path.exists() {
289 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700290 value: value.unwrap().to_owned(),
291 expected: "this disk path does not exist",
292 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700293 }
294 if name == "root" {
Daniel Verkampaac28132018-10-15 14:58:48 -0700295 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -0700296 return Err(argument::Error::TooManyArguments(
297 "ran out of letters for to assign to root disk".to_owned(),
298 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700299 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700300 cfg.params.push(format!(
301 "root=/dev/vd{} ro",
David Tolnay5bbbf612018-12-01 17:49:30 -0800302 char::from(b'a' + cfg.disks.len() as u8)
Zach Reizner55a9e502018-10-03 10:22:32 -0700303 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700304 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700305 cfg.disks.push(DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -0700306 path: disk_path,
307 read_only: !name.starts_with("rw"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700308 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700309 }
310 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700311 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700312 return Err(argument::Error::TooManyArguments(
313 "`host_ip` already given".to_owned(),
314 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700315 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700316 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -0700317 Some(
318 value
319 .unwrap()
320 .parse()
321 .map_err(|_| argument::Error::InvalidValue {
322 value: value.unwrap().to_owned(),
323 expected: "`host_ip` needs to be in the form \"x.x.x.x\"",
324 })?,
325 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700326 }
327 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700328 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700329 return Err(argument::Error::TooManyArguments(
330 "`netmask` already given".to_owned(),
331 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700332 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700333 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -0700334 Some(
335 value
336 .unwrap()
337 .parse()
338 .map_err(|_| argument::Error::InvalidValue {
339 value: value.unwrap().to_owned(),
340 expected: "`netmask` needs to be in the form \"x.x.x.x\"",
341 })?,
342 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700343 }
344 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700345 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700346 return Err(argument::Error::TooManyArguments(
347 "`mac` already given".to_owned(),
348 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700349 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700350 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -0700351 Some(
352 value
353 .unwrap()
354 .parse()
355 .map_err(|_| argument::Error::InvalidValue {
356 value: value.unwrap().to_owned(),
357 expected: "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
358 })?,
359 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700360 }
Stephen Barber28a5a612017-10-20 17:15:30 -0700361 "wayland-sock" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700362 if cfg.wayland_socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700363 return Err(argument::Error::TooManyArguments(
364 "`wayland-sock` already given".to_owned(),
365 ));
Stephen Barber28a5a612017-10-20 17:15:30 -0700366 }
367 let wayland_socket_path = PathBuf::from(value.unwrap());
368 if !wayland_socket_path.exists() {
369 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700370 value: value.unwrap().to_string(),
371 expected: "Wayland socket does not exist",
372 });
Stephen Barber28a5a612017-10-20 17:15:30 -0700373 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700374 cfg.wayland_socket_path = Some(wayland_socket_path);
Stephen Barber28a5a612017-10-20 17:15:30 -0700375 }
David Reveman52ba4e52018-04-22 21:42:09 -0400376 #[cfg(feature = "wl-dmabuf")]
Daniel Verkampaac28132018-10-15 14:58:48 -0700377 "wayland-dmabuf" => cfg.wayland_dmabuf = true,
Zach Reiznerefe95782017-08-26 18:05:48 -0700378 "socket" => {
379 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700380 return Err(argument::Error::TooManyArguments(
381 "`socket` already given".to_owned(),
382 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700383 }
384 let mut socket_path = PathBuf::from(value.unwrap());
385 if socket_path.is_dir() {
386 socket_path.push(format!("crosvm-{}.sock", getpid()));
387 }
388 if socket_path.exists() {
389 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700390 value: socket_path.to_string_lossy().into_owned(),
391 expected: "this socket path already exists",
392 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700393 }
394 cfg.socket_path = Some(socket_path);
395 }
396 "multiprocess" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700397 cfg.multiprocess = true;
Zach Reiznerefe95782017-08-26 18:05:48 -0700398 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700399 "disable-sandbox" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700400 cfg.multiprocess = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700401 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700402 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700403 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700404 return Err(argument::Error::TooManyArguments(
405 "`cid` alread given".to_owned(),
406 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700407 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700408 cfg.cid = Some(
409 value
410 .unwrap()
411 .parse()
412 .map_err(|_| argument::Error::InvalidValue {
413 value: value.unwrap().to_owned(),
414 expected: "this value for `cid` must be an unsigned integer",
415 })?,
416 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700417 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700418 "shared-dir" => {
419 // Formatted as <src:tag>.
420 let param = value.unwrap();
421 let mut components = param.splitn(2, ':');
Zach Reizner55a9e502018-10-03 10:22:32 -0700422 let src =
423 PathBuf::from(
424 components
425 .next()
426 .ok_or_else(|| argument::Error::InvalidValue {
427 value: param.to_owned(),
428 expected: "missing source path for `shared-dir`",
429 })?,
430 );
431 let tag = components
432 .next()
433 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700434 value: param.to_owned(),
435 expected: "missing tag for `shared-dir`",
David Tolnay2bac1e72018-12-12 14:33:42 -0800436 })?
437 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700438
439 if !src.is_dir() {
440 return Err(argument::Error::InvalidValue {
441 value: param.to_owned(),
442 expected: "source path for `shared-dir` must be a directory",
443 });
444 }
445
Daniel Verkampaac28132018-10-15 14:58:48 -0700446 cfg.shared_dirs.push((src, tag));
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700447 }
Dylan Reide026ef02017-10-02 19:03:52 -0700448 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700449 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -0700450 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -0700451 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800452 "plugin" => {
453 if !cfg.kernel_path.as_os_str().is_empty() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700454 return Err(argument::Error::TooManyArguments(
455 "`plugin` can not be used with kernel".to_owned(),
456 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800457 } else if cfg.plugin.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700458 return Err(argument::Error::TooManyArguments(
459 "`plugin` already given".to_owned(),
460 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800461 }
Zach Reiznercc30d582018-01-23 21:16:42 -0800462 let plugin = PathBuf::from(value.unwrap().to_owned());
463 if plugin.is_relative() {
464 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700465 value: plugin.to_string_lossy().into_owned(),
466 expected: "the plugin path must be an absolute path",
467 });
Zach Reiznercc30d582018-01-23 21:16:42 -0800468 }
469 cfg.plugin = Some(plugin);
Zach Reizner55a9e502018-10-03 10:22:32 -0700470 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700471 "plugin-root" => {
472 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -0700473 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800474 "plugin-mount" => {
475 let components: Vec<&str> = value.unwrap().split(":").collect();
476 if components.len() != 3 {
477 return Err(argument::Error::InvalidValue {
478 value: value.unwrap().to_owned(),
479 expected:
480 "`plugin-mount` must have exactly 3 components: <src>:<dst>:<writable>",
481 });
482 }
483
484 let src = PathBuf::from(components[0]);
485 if src.is_relative() {
486 return Err(argument::Error::InvalidValue {
487 value: components[0].to_owned(),
488 expected: "the source path for `plugin-mount` must be absolute",
489 });
490 }
491 if !src.exists() {
492 return Err(argument::Error::InvalidValue {
493 value: components[0].to_owned(),
494 expected: "the source path for `plugin-mount` does not exist",
495 });
496 }
497
498 let dst = PathBuf::from(components[1]);
499 if dst.is_relative() {
500 return Err(argument::Error::InvalidValue {
501 value: components[1].to_owned(),
502 expected: "the destination path for `plugin-mount` must be absolute",
503 });
504 }
505
506 let writable: bool =
507 components[2]
508 .parse()
509 .map_err(|_| argument::Error::InvalidValue {
510 value: components[2].to_owned(),
511 expected: "the <writable> component for `plugin-mount` is not valid bool",
512 })?;
513
514 cfg.plugin_mounts.push(BindMount { src, dst, writable });
515 }
Dmitry Torokhov1a6262b2019-03-01 00:34:03 -0800516 "plugin-gid-map" => {
517 let components: Vec<&str> = value.unwrap().split(":").collect();
518 if components.len() != 3 {
519 return Err(argument::Error::InvalidValue {
520 value: value.unwrap().to_owned(),
521 expected:
522 "`plugin-gid-map` must have exactly 3 components: <inner>:<outer>:<count>",
523 });
524 }
525
526 let inner: libc::gid_t =
527 components[0]
528 .parse()
529 .map_err(|_| argument::Error::InvalidValue {
530 value: components[0].to_owned(),
531 expected: "the <inner> component for `plugin-gid-map` is not valid gid",
532 })?;
533
534 let outer: libc::gid_t =
535 components[1]
536 .parse()
537 .map_err(|_| argument::Error::InvalidValue {
538 value: components[1].to_owned(),
539 expected: "the <outer> component for `plugin-gid-map` is not valid gid",
540 })?;
541
542 let count: u32 = components[2]
543 .parse()
544 .map_err(|_| argument::Error::InvalidValue {
545 value: components[2].to_owned(),
546 expected: "the <count> component for `plugin-gid-map` is not valid number",
547 })?;
548
549 cfg.plugin_gid_maps.push(GidMap {
550 inner,
551 outer,
552 count,
553 });
554 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700555 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700556 "tap-fd" => {
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800557 cfg.tap_fd.push(
558 value
559 .unwrap()
560 .parse()
561 .map_err(|_| argument::Error::InvalidValue {
562 value: value.unwrap().to_owned(),
563 expected: "this value for `tap-fd` must be an unsigned integer",
564 })?,
565 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700566 }
Zach Reizner3a8100a2017-09-13 19:15:43 -0700567 "gpu" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700568 cfg.gpu = true;
Zach Reizner3a8100a2017-09-13 19:15:43 -0700569 }
David Tolnay43f8e212019-02-13 17:28:16 -0800570 "software-tpm" => {
571 cfg.software_tpm = true;
572 }
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800573 "trackpad" => {
574 if cfg.virtio_trackpad.is_some() {
575 return Err(argument::Error::TooManyArguments(
576 "`trackpad` already given".to_owned(),
577 ));
578 }
579 let mut it = value.unwrap().split(":");
580
581 let mut trackpad_spec =
582 TrackpadOption::new(PathBuf::from(it.next().unwrap().to_owned()));
583 if let Some(width) = it.next() {
584 trackpad_spec.width = width.trim().parse().unwrap();
585 }
586 if let Some(height) = it.next() {
587 trackpad_spec.height = height.trim().parse().unwrap();
588 }
589
590 cfg.virtio_trackpad = Some(trackpad_spec);
591 }
592 "mouse" => {
593 if cfg.virtio_mouse.is_some() {
594 return Err(argument::Error::TooManyArguments(
595 "`mouse` already given".to_owned(),
596 ));
597 }
598 cfg.virtio_mouse = Some(PathBuf::from(value.unwrap().to_owned()));
599 }
600 "keyboard" => {
601 if cfg.virtio_keyboard.is_some() {
602 return Err(argument::Error::TooManyArguments(
603 "`keyboard` already given".to_owned(),
604 ));
605 }
606 cfg.virtio_keyboard = Some(PathBuf::from(value.unwrap().to_owned()));
607 }
608 "evdev" => {
609 let dev_path = PathBuf::from(value.unwrap());
610 if !dev_path.exists() {
611 return Err(argument::Error::InvalidValue {
612 value: value.unwrap().to_owned(),
613 expected: "this input device path does not exist",
614 });
615 }
616 cfg.virtio_input_evdevs.push(dev_path);
617 }
Miriam Zimmerman26ac9282019-01-29 21:21:48 -0800618 "split-irqchip" => {
619 cfg.split_irqchip = true;
620 }
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800621 "initrd" => {
622 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
623 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700624 "help" => return Err(argument::Error::PrintHelp),
625 _ => unreachable!(),
626 }
627 Ok(())
628}
629
Dylan Reidbfba9932018-02-05 15:51:59 -0800630fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -0700631 let arguments =
632 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800633 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Daniel Verkampe403f5c2018-12-11 16:29:26 -0800634 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700635 Argument::short_value('p',
636 "params",
637 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -0800638 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700639 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
640 Argument::short_value('m',
641 "mem",
642 "N",
643 "Amount of guest memory in MiB. (default: 256)"),
644 Argument::short_value('r',
645 "root",
646 "PATH",
647 "Path to a root disk image. Like `--disk` but adds appropriate kernel command line option."),
648 Argument::short_value('d', "disk", "PATH", "Path to a disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700649 Argument::value("qcow", "PATH", "Path to a qcow2 disk image. (Deprecated; use --disk instead.)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700650 Argument::value("rwdisk", "PATH", "Path to a writable disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700651 Argument::value("rwqcow", "PATH", "Path to a writable qcow2 disk image. (Deprecated; use --rwdisk instead.)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700652 Argument::value("host_ip",
653 "IP",
654 "IP address to assign to host tap interface."),
655 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
656 Argument::value("mac", "MAC", "MAC address for VM."),
paulhsiaf052cfe2019-01-22 15:22:25 +0800657 Argument::flag("cras-audio", "Add an audio device to the VM that plays samples through CRAS server"),
Dylan Reid3082e8e2019-01-07 10:33:48 -0800658 Argument::flag("null-audio", "Add an audio device to the VM that plays samples to /dev/null"),
Stephen Barber28a5a612017-10-20 17:15:30 -0700659 Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
David Reveman52ba4e52018-04-22 21:42:09 -0400660 #[cfg(feature = "wl-dmabuf")]
661 Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700662 Argument::short_value('s',
663 "socket",
664 "PATH",
665 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700666 Argument::short_flag('u', "multiprocess", "Run each device in a child process(default)."),
667 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700668 Argument::value("cid", "CID", "Context ID for virtual sockets."),
669 Argument::value("shared-dir", "PATH:TAG",
670 "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 -0700671 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner8864cb02018-01-16 17:59:03 -0800672 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -0800673 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -0800674 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700675 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 -0800676 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800677 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 -0800678 #[cfg(feature = "plugin")]
679 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 +0000680 Argument::flag("vhost-net", "Use vhost for networking."),
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700681 Argument::value("tap-fd",
682 "fd",
Jorge E. Moreirab7952802019-02-12 16:43:05 -0800683 "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 -0700684 #[cfg(feature = "gpu")]
685 Argument::flag("gpu", "(EXPERIMENTAL) enable virtio-gpu device"),
David Tolnay43f8e212019-02-13 17:28:16 -0800686 #[cfg(feature = "tpm")]
687 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
Jorge E. Moreiradffec502019-01-14 18:44:49 -0800688 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"),
689 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)."),
690 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
691 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 -0800692 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
693 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700694 Argument::short_flag('h', "help", "Print help message.")];
695
696 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -0700697 let match_res = set_arguments(args, &arguments[..], |name, value| {
698 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -0800699 })
700 .and_then(|_| {
Zach Reizner8864cb02018-01-16 17:59:03 -0800701 if cfg.kernel_path.as_os_str().is_empty() && cfg.plugin.is_none() {
Zach Reiznerefe95782017-08-26 18:05:48 -0700702 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
703 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700704 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
705 if cfg.host_ip.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700706 return Err(argument::Error::ExpectedArgument(
707 "`host_ip` missing from network config".to_owned(),
708 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700709 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700710 if cfg.netmask.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700711 return Err(argument::Error::ExpectedArgument(
712 "`netmask` missing from network config".to_owned(),
713 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700714 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700715 if cfg.mac_address.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700716 return Err(argument::Error::ExpectedArgument(
717 "`mac` missing from network config".to_owned(),
718 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700719 }
720 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700721 if cfg.plugin_root.is_some() && cfg.plugin.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700722 return Err(argument::Error::ExpectedArgument(
723 "`plugin-root` requires `plugin`".to_owned(),
724 ));
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700725 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700726 Ok(())
727 });
728
729 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -0800730 #[cfg(feature = "plugin")]
David Tolnay2bac1e72018-12-12 14:33:42 -0800731 Ok(()) if cfg.plugin.is_some() => match plugin::run_config(cfg) {
732 Ok(_) => {
733 info!("crosvm and plugin have exited normally");
734 Ok(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800735 }
David Tolnay2bac1e72018-12-12 14:33:42 -0800736 Err(e) => {
737 error!("{}", e);
738 Err(())
739 }
740 },
Zach Reizner55a9e502018-10-03 10:22:32 -0700741 Ok(()) => match linux::run_config(cfg) {
742 Ok(_) => {
743 info!("crosvm has exited normally");
744 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -0700745 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700746 Err(e) => {
747 error!("{}", e);
748 Err(())
749 }
750 },
Dylan Reidbfba9932018-02-05 15:51:59 -0800751 Err(argument::Error::PrintHelp) => {
752 print_help("crosvm run", "KERNEL", &arguments[..]);
753 Ok(())
754 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800755 Err(e) => {
756 println!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -0800757 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800758 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700759 }
760}
761
Zach Reizner78986322019-02-21 20:43:21 -0800762fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()> {
Dylan Reidbfba9932018-02-05 15:51:59 -0800763 let mut return_result = Ok(());
Zach Reiznerefe95782017-08-26 18:05:48 -0700764 for socket_path in args {
Zach Reiznera60744b2019-02-13 17:33:32 -0800765 match UnixSeqpacket::connect(&socket_path) {
Zach Reiznerefe95782017-08-26 18:05:48 -0700766 Ok(s) => {
Zach Reizner78986322019-02-21 20:43:21 -0800767 let socket = MsgSocket::<VmRequest, VmResponse>::new(s);
768 if let Err(e) = socket.send(request) {
Zach Reizner55a9e502018-10-03 10:22:32 -0700769 error!(
David Tolnayb4bd00f2019-02-12 17:51:26 -0800770 "failed to send request to socket at '{}': {}",
Zach Reizner55a9e502018-10-03 10:22:32 -0700771 socket_path, e
772 );
Zach Reizner78986322019-02-21 20:43:21 -0800773 return_result = Err(());
774 continue;
Zach Reiznerefe95782017-08-26 18:05:48 -0700775 }
Zach Reizner78986322019-02-21 20:43:21 -0800776 match socket.recv() {
777 Ok(response) => info!("request response was {}", response),
778 Err(e) => {
779 error!(
780 "failed to send request to socket at2 '{}': {}",
781 socket_path, e
782 );
783 return_result = Err(());
784 continue;
785 }
Dylan Reidd4432042017-12-06 18:20:09 -0800786 }
787 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800788 Err(e) => {
789 error!("failed to connect to socket at '{}': {}", socket_path, e);
790 return_result = Err(());
791 }
Dylan Reidd4432042017-12-06 18:20:09 -0800792 }
793 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800794
795 return_result
Dylan Reidd4432042017-12-06 18:20:09 -0800796}
Zach Reiznerefe95782017-08-26 18:05:48 -0700797
Zach Reizner78986322019-02-21 20:43:21 -0800798fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> {
799 if args.len() == 0 {
800 print_help("crosvm stop", "VM_SOCKET...", &[]);
801 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800802 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -0800803 }
804 vms_request(&VmRequest::Exit, args)
805}
806
807fn suspend_vms(args: std::env::Args) -> std::result::Result<(), ()> {
808 if args.len() == 0 {
809 print_help("crosvm suspend", "VM_SOCKET...", &[]);
810 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800811 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -0800812 }
813 vms_request(&VmRequest::Suspend, args)
814}
815
816fn resume_vms(args: std::env::Args) -> std::result::Result<(), ()> {
817 if args.len() == 0 {
818 print_help("crosvm resume", "VM_SOCKET...", &[]);
819 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800820 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -0800821 }
822 vms_request(&VmRequest::Resume, args)
823}
824
825fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
826 if args.len() < 2 {
827 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
828 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800829 return Err(());
Zach Reizner78986322019-02-21 20:43:21 -0800830 }
831 let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
832 Ok(n) => n,
833 Err(_) => {
834 error!("Failed to parse number of bytes");
835 return Err(());
836 }
837 };
838
839 vms_request(&VmRequest::BalloonAdjust(num_bytes), args)
840}
841
Dylan Reid2dcb6322018-07-13 10:42:48 -0700842fn create_qcow2(mut args: std::env::Args) -> std::result::Result<(), ()> {
843 if args.len() != 2 {
844 print_help("crosvm create_qcow2", "PATH SIZE", &[]);
Dylan Reid940259c2018-07-20 14:22:33 -0700845 println!("Create a new QCOW2 image at `PATH` of the specified `SIZE` in bytes.");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800846 return Err(());
Dylan Reid2dcb6322018-07-13 10:42:48 -0700847 }
848 let file_path = args.nth(0).unwrap();
849 let size: u64 = match args.nth(0).unwrap().parse::<u64>() {
850 Ok(n) => n,
851 Err(_) => {
852 error!("Failed to parse size of the disk.");
853 return Err(());
Zach Reizner55a9e502018-10-03 10:22:32 -0700854 }
Dylan Reid2dcb6322018-07-13 10:42:48 -0700855 };
856
857 let file = OpenOptions::new()
858 .create(true)
859 .read(true)
860 .write(true)
861 .open(&file_path)
862 .map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800863 error!("Failed opening qcow file at '{}': {}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -0700864 })?;
865
Zach Reizner55a9e502018-10-03 10:22:32 -0700866 QcowFile::new(file, size).map_err(|e| {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800867 error!("Failed to create qcow file at '{}': {}", file_path, e);
Zach Reizner55a9e502018-10-03 10:22:32 -0700868 })?;
Dylan Reid2dcb6322018-07-13 10:42:48 -0700869
870 Ok(())
871}
872
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800873fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
874 if args.len() < 2 {
875 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
876 println!("Manage attached virtual disk devices.");
877 println!("Subcommands:");
878 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
Jianxun Zhang56497d22019-03-04 14:38:24 -0800879 return Err(());
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800880 }
881 let subcommand: &str = &args.nth(0).unwrap();
882
883 let request = match subcommand {
884 "resize" => {
885 let disk_index = match args.nth(0).unwrap().parse::<usize>() {
886 Ok(n) => n,
887 Err(_) => {
888 error!("Failed to parse disk index");
889 return Err(());
890 }
891 };
892
893 let new_size = match args.nth(0).unwrap().parse::<u64>() {
894 Ok(n) => n,
895 Err(_) => {
896 error!("Failed to parse disk size");
897 return Err(());
898 }
899 };
900
901 VmRequest::DiskResize {
902 disk_index,
903 new_size,
904 }
905 }
906 _ => {
907 error!("Unknown disk subcommand '{}'", subcommand);
908 return Err(());
909 }
910 };
911
Zach Reizner78986322019-02-21 20:43:21 -0800912 vms_request(&request, args)
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800913}
914
Zach Reiznerefe95782017-08-26 18:05:48 -0700915fn print_usage() {
916 print_help("crosvm", "[stop|run]", &[]);
917 println!("Commands:");
918 println!(" stop - Stops crosvm instances via their control sockets.");
919 println!(" run - Start a new crosvm instance.");
Dylan Reid2dcb6322018-07-13 10:42:48 -0700920 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800921 println!(" disk - Manage attached virtual disk devices.")
Zach Reiznerefe95782017-08-26 18:05:48 -0700922}
923
Dylan Reidbfba9932018-02-05 15:51:59 -0800924fn crosvm_main() -> std::result::Result<(), ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -0700925 if let Err(e) = syslog::init() {
David Tolnayb4bd00f2019-02-12 17:51:26 -0800926 println!("failed to initialize syslog: {}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -0800927 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -0700928 }
Zach Reizner639d9672017-05-01 17:57:18 -0700929
Zach Reiznerb3fa5c92019-01-28 14:05:23 -0800930 panic_hook::set_panic_hook();
931
Zach Reiznerefe95782017-08-26 18:05:48 -0700932 let mut args = std::env::args();
933 if args.next().is_none() {
934 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -0800935 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -0700936 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700937
Zach Reizner8864cb02018-01-16 17:59:03 -0800938 // Past this point, usage of exit is in danger of leaking zombie processes.
Dylan Reidbfba9932018-02-05 15:51:59 -0800939 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
940 None => {
941 print_usage();
942 Ok(())
943 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700944 Some("stop") => stop_vms(args),
Zach Reizner6a8fdd92019-01-16 14:38:41 -0800945 Some("suspend") => suspend_vms(args),
946 Some("resume") => resume_vms(args),
Zach Reizner55a9e502018-10-03 10:22:32 -0700947 Some("run") => run_vm(args),
948 Some("balloon") => balloon_vms(args),
949 Some("create_qcow2") => create_qcow2(args),
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800950 Some("disk") => disk_cmd(args),
Zach Reiznerefe95782017-08-26 18:05:48 -0700951 Some(c) => {
952 println!("invalid subcommand: {:?}", c);
953 print_usage();
Dylan Reidbfba9932018-02-05 15:51:59 -0800954 Err(())
Zach Reiznerefe95782017-08-26 18:05:48 -0700955 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800956 };
Zach Reiznerefe95782017-08-26 18:05:48 -0700957
958 // Reap exit status from any child device processes. At this point, all devices should have been
959 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
960 // take some time for the processes to shut down.
961 if !wait_all_children() {
962 // We gave them a chance, and it's too late.
963 warn!("not all child processes have exited; sending SIGKILL");
964 if let Err(e) = kill_process_group() {
965 // We're now at the mercy of the OS to clean up after us.
David Tolnayb4bd00f2019-02-12 17:51:26 -0800966 warn!("unable to kill all child processes: {}", e);
Zach Reiznerefe95782017-08-26 18:05:48 -0700967 }
968 }
969
970 // WARNING: Any code added after this point is not guaranteed to run
971 // since we may forcibly kill this process (and its children) above.
Dylan Reidbfba9932018-02-05 15:51:59 -0800972 ret
973}
974
975fn main() {
976 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
Zach Reizner639d9672017-05-01 17:57:18 -0700977}