blob: 939236018d13ec7b123f3ee051bba1bc53732f59 [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 Reizner8864cb02018-01-16 17:59:03 -080042#[cfg(feature = "plugin")]
43pub mod plugin;
Zach Reizner29ad3c72017-08-04 15:12:58 -070044
Dylan Reid2dcb6322018-07-13 10:42:48 -070045use std::fs::OpenOptions;
Stephen Barber2cfc2052017-06-21 15:16:11 -070046use std::net;
Chirantan Ekbote5f787212018-05-31 15:31:31 -070047use std::os::unix::io::RawFd;
Zach Reizner29ad3c72017-08-04 15:12:58 -070048use std::os::unix::net::UnixDatagram;
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 Reizner55a9e502018-10-03 10:22:32 -070055use sys_util::{getpid, kill_process_group, 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};
Jingkui Wange13b1802018-10-03 13:04:47 -070058use msg_socket::{MsgSender, Sender};
Zach Reizner39aa26b2017-12-12 18:03:23 -080059use vm_control::VmRequest;
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
Daniel Verkampaac28132018-10-15 14:58:48 -070075pub struct Config {
76 vcpu_count: Option<u32>,
77 memory: Option<usize>,
78 kernel_path: PathBuf,
Tristan Muntsinger4133b012018-12-21 16:01:56 -080079 android_fstab: Option<PathBuf>,
Daniel Verkampaac28132018-10-15 14:58:48 -070080 params: Vec<String>,
81 socket_path: Option<PathBuf>,
82 plugin: Option<PathBuf>,
83 plugin_root: Option<PathBuf>,
Chirantan Ekboted41d7262018-11-16 16:37:45 -080084 plugin_mounts: Vec<BindMount>,
Zach Reiznerefe95782017-08-26 18:05:48 -070085 disks: Vec<DiskOption>,
Stephen Barber2cfc2052017-06-21 15:16:11 -070086 host_ip: Option<net::Ipv4Addr>,
87 netmask: Option<net::Ipv4Addr>,
Stephen Barber308ff602018-02-13 22:47:07 -080088 mac_address: Option<net_util::MacAddress>,
Stephen Barber5e77e882017-08-07 17:13:38 -070089 vhost_net: bool,
Chirantan Ekbote5f787212018-05-31 15:31:31 -070090 tap_fd: Option<RawFd>,
Dylan Reid059a1882018-07-23 17:58:09 -070091 cid: Option<u64>,
Stephen Barber28a5a612017-10-20 17:15:30 -070092 wayland_socket_path: Option<PathBuf>,
David Reveman52ba4e52018-04-22 21:42:09 -040093 wayland_dmabuf: bool,
Chirantan Ekboteebd56812018-04-16 19:32:04 -070094 shared_dirs: Vec<(PathBuf, String)>,
Zach Reizner639d9672017-05-01 17:57:18 -070095 multiprocess: bool,
Dylan Reidd0c9adc2017-10-02 19:04:50 -070096 seccomp_policy_dir: PathBuf,
Zach Reizner3a8100a2017-09-13 19:15:43 -070097 gpu: bool,
paulhsiaf052cfe2019-01-22 15:22:25 +080098 cras_audio: bool,
Dylan Reid3082e8e2019-01-07 10:33:48 -080099 null_audio: bool,
Dylan Reid059a1882018-07-23 17:58:09 -0700100}
101
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700102impl Default for Config {
103 fn default() -> Config {
104 Config {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700105 vcpu_count: None,
106 memory: None,
107 kernel_path: PathBuf::default(),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800108 android_fstab: None,
Zach Reiznerbb678712018-01-30 18:13:04 -0800109 params: Vec::new(),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700110 socket_path: None,
Zach Reizner8864cb02018-01-16 17:59:03 -0800111 plugin: None,
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700112 plugin_root: None,
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800113 plugin_mounts: Vec::new(),
Daniel Verkampaac28132018-10-15 14:58:48 -0700114 disks: Vec::new(),
115 host_ip: None,
116 netmask: None,
117 mac_address: None,
118 vhost_net: false,
119 tap_fd: None,
120 cid: None,
121 gpu: false,
122 wayland_socket_path: None,
123 wayland_dmabuf: false,
124 shared_dirs: Vec::new(),
125 multiprocess: !cfg!(feature = "default-no-sandbox"),
126 seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
paulhsiaf052cfe2019-01-22 15:22:25 +0800127 cras_audio: false,
Dylan Reid3082e8e2019-01-07 10:33:48 -0800128 null_audio: false,
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700129 }
130 }
131}
132
Stephen Barbera00753b2017-07-18 13:57:26 -0700133// Wait for all children to exit. Return true if they have all exited, false
134// otherwise.
135fn wait_all_children() -> bool {
Stephen Barber49dd2e22018-10-29 18:29:58 -0700136 const CHILD_WAIT_MAX_ITER: isize = 100;
Stephen Barbera00753b2017-07-18 13:57:26 -0700137 const CHILD_WAIT_MS: u64 = 10;
138 for _ in 0..CHILD_WAIT_MAX_ITER {
Stephen Barbera00753b2017-07-18 13:57:26 -0700139 loop {
Zach Reizner56158c82017-08-24 13:50:14 -0700140 match reap_child() {
141 Ok(0) => break,
142 // We expect ECHILD which indicates that there were no children left.
143 Err(e) if e.errno() == libc::ECHILD => return true,
144 Err(e) => {
145 warn!("error while waiting for children: {:?}", e);
146 return false;
Stephen Barbera00753b2017-07-18 13:57:26 -0700147 }
Zach Reizner56158c82017-08-24 13:50:14 -0700148 // We reaped one child, so continue reaping.
Zach Reizner55a9e502018-10-03 10:22:32 -0700149 _ => {}
Stephen Barbera00753b2017-07-18 13:57:26 -0700150 }
151 }
Zach Reizner56158c82017-08-24 13:50:14 -0700152 // There's no timeout option for waitpid which reap_child calls internally, so our only
153 // recourse is to sleep while waiting for the children to exit.
Stephen Barbera00753b2017-07-18 13:57:26 -0700154 sleep(Duration::from_millis(CHILD_WAIT_MS));
155 }
156
157 // If we've made it to this point, not all of the children have exited.
David Tolnay5bbbf612018-12-01 17:49:30 -0800158 false
Stephen Barbera00753b2017-07-18 13:57:26 -0700159}
160
Zach Reiznerefe95782017-08-26 18:05:48 -0700161fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
162 match name {
163 "" => {
Zach Reizner8864cb02018-01-16 17:59:03 -0800164 if cfg.plugin.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700165 return Err(argument::Error::TooManyArguments(
166 "`plugin` can not be used with kernel".to_owned(),
167 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800168 } else if !cfg.kernel_path.as_os_str().is_empty() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700169 return Err(argument::Error::TooManyArguments(
170 "expected exactly one kernel path".to_owned(),
171 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700172 } else {
173 let kernel_path = PathBuf::from(value.unwrap());
174 if !kernel_path.exists() {
175 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700176 value: value.unwrap().to_owned(),
177 expected: "this kernel path does not exist",
178 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700179 }
180 cfg.kernel_path = kernel_path;
181 }
182 }
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800183 "android-fstab" => {
184 if cfg.android_fstab.is_some()
185 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
186 {
187 return Err(argument::Error::TooManyArguments(
188 "expected exactly one android fstab path".to_owned(),
189 ));
190 } else {
191 let android_fstab = PathBuf::from(value.unwrap());
192 if !android_fstab.exists() {
193 return Err(argument::Error::InvalidValue {
194 value: value.unwrap().to_owned(),
195 expected: "this android fstab path does not exist",
196 });
197 }
198 cfg.android_fstab = Some(android_fstab);
199 }
200 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700201 "params" => {
Zach Reiznerbb678712018-01-30 18:13:04 -0800202 cfg.params.push(value.unwrap().to_owned());
Zach Reiznerefe95782017-08-26 18:05:48 -0700203 }
204 "cpus" => {
205 if cfg.vcpu_count.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700206 return Err(argument::Error::TooManyArguments(
207 "`cpus` already given".to_owned(),
208 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700209 }
210 cfg.vcpu_count =
Zach Reizner55a9e502018-10-03 10:22:32 -0700211 Some(
212 value
213 .unwrap()
214 .parse()
215 .map_err(|_| argument::Error::InvalidValue {
216 value: value.unwrap().to_owned(),
217 expected: "this value for `cpus` needs to be integer",
218 })?,
219 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700220 }
221 "mem" => {
222 if cfg.memory.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700223 return Err(argument::Error::TooManyArguments(
224 "`mem` already given".to_owned(),
225 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700226 }
227 cfg.memory =
Zach Reizner55a9e502018-10-03 10:22:32 -0700228 Some(
229 value
230 .unwrap()
231 .parse()
232 .map_err(|_| argument::Error::InvalidValue {
233 value: value.unwrap().to_owned(),
234 expected: "this value for `mem` needs to be integer",
235 })?,
236 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700237 }
paulhsiaf052cfe2019-01-22 15:22:25 +0800238 "cras-audio" => {
239 cfg.cras_audio = true;
240 }
Dylan Reid3082e8e2019-01-07 10:33:48 -0800241 "null-audio" => {
242 cfg.null_audio = true;
243 }
Dylan Reid88624f82018-01-11 09:20:16 -0800244 "root" | "disk" | "rwdisk" | "qcow" | "rwqcow" => {
Zach Reiznerefe95782017-08-26 18:05:48 -0700245 let disk_path = PathBuf::from(value.unwrap());
246 if !disk_path.exists() {
247 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700248 value: value.unwrap().to_owned(),
249 expected: "this disk path does not exist",
250 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700251 }
252 if name == "root" {
Daniel Verkampaac28132018-10-15 14:58:48 -0700253 if cfg.disks.len() >= 26 {
Zach Reizner55a9e502018-10-03 10:22:32 -0700254 return Err(argument::Error::TooManyArguments(
255 "ran out of letters for to assign to root disk".to_owned(),
256 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700257 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700258 cfg.params.push(format!(
259 "root=/dev/vd{} ro",
David Tolnay5bbbf612018-12-01 17:49:30 -0800260 char::from(b'a' + cfg.disks.len() as u8)
Zach Reizner55a9e502018-10-03 10:22:32 -0700261 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700262 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700263 cfg.disks.push(DiskOption {
Zach Reizner55a9e502018-10-03 10:22:32 -0700264 path: disk_path,
265 read_only: !name.starts_with("rw"),
Zach Reizner55a9e502018-10-03 10:22:32 -0700266 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700267 }
268 "host_ip" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700269 if cfg.host_ip.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700270 return Err(argument::Error::TooManyArguments(
271 "`host_ip` already given".to_owned(),
272 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700273 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700274 cfg.host_ip =
Zach Reizner55a9e502018-10-03 10:22:32 -0700275 Some(
276 value
277 .unwrap()
278 .parse()
279 .map_err(|_| argument::Error::InvalidValue {
280 value: value.unwrap().to_owned(),
281 expected: "`host_ip` needs to be in the form \"x.x.x.x\"",
282 })?,
283 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700284 }
285 "netmask" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700286 if cfg.netmask.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700287 return Err(argument::Error::TooManyArguments(
288 "`netmask` already given".to_owned(),
289 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700290 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700291 cfg.netmask =
Zach Reizner55a9e502018-10-03 10:22:32 -0700292 Some(
293 value
294 .unwrap()
295 .parse()
296 .map_err(|_| argument::Error::InvalidValue {
297 value: value.unwrap().to_owned(),
298 expected: "`netmask` needs to be in the form \"x.x.x.x\"",
299 })?,
300 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700301 }
302 "mac" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700303 if cfg.mac_address.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700304 return Err(argument::Error::TooManyArguments(
305 "`mac` already given".to_owned(),
306 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700307 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700308 cfg.mac_address =
Zach Reizner55a9e502018-10-03 10:22:32 -0700309 Some(
310 value
311 .unwrap()
312 .parse()
313 .map_err(|_| argument::Error::InvalidValue {
314 value: value.unwrap().to_owned(),
315 expected: "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
316 })?,
317 )
Zach Reiznerefe95782017-08-26 18:05:48 -0700318 }
Stephen Barber28a5a612017-10-20 17:15:30 -0700319 "wayland-sock" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700320 if cfg.wayland_socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700321 return Err(argument::Error::TooManyArguments(
322 "`wayland-sock` already given".to_owned(),
323 ));
Stephen Barber28a5a612017-10-20 17:15:30 -0700324 }
325 let wayland_socket_path = PathBuf::from(value.unwrap());
326 if !wayland_socket_path.exists() {
327 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700328 value: value.unwrap().to_string(),
329 expected: "Wayland socket does not exist",
330 });
Stephen Barber28a5a612017-10-20 17:15:30 -0700331 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700332 cfg.wayland_socket_path = Some(wayland_socket_path);
Stephen Barber28a5a612017-10-20 17:15:30 -0700333 }
David Reveman52ba4e52018-04-22 21:42:09 -0400334 #[cfg(feature = "wl-dmabuf")]
Daniel Verkampaac28132018-10-15 14:58:48 -0700335 "wayland-dmabuf" => cfg.wayland_dmabuf = true,
Zach Reiznerefe95782017-08-26 18:05:48 -0700336 "socket" => {
337 if cfg.socket_path.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700338 return Err(argument::Error::TooManyArguments(
339 "`socket` already given".to_owned(),
340 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700341 }
342 let mut socket_path = PathBuf::from(value.unwrap());
343 if socket_path.is_dir() {
344 socket_path.push(format!("crosvm-{}.sock", getpid()));
345 }
346 if socket_path.exists() {
347 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700348 value: socket_path.to_string_lossy().into_owned(),
349 expected: "this socket path already exists",
350 });
Zach Reiznerefe95782017-08-26 18:05:48 -0700351 }
352 cfg.socket_path = Some(socket_path);
353 }
354 "multiprocess" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700355 cfg.multiprocess = true;
Zach Reiznerefe95782017-08-26 18:05:48 -0700356 }
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700357 "disable-sandbox" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700358 cfg.multiprocess = false;
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700359 }
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700360 "cid" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700361 if cfg.cid.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700362 return Err(argument::Error::TooManyArguments(
363 "`cid` alread given".to_owned(),
364 ));
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700365 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700366 cfg.cid = Some(
367 value
368 .unwrap()
369 .parse()
370 .map_err(|_| argument::Error::InvalidValue {
371 value: value.unwrap().to_owned(),
372 expected: "this value for `cid` must be an unsigned integer",
373 })?,
374 );
Chirantan Ekbote88f9cba2017-08-28 09:51:18 -0700375 }
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700376 "shared-dir" => {
377 // Formatted as <src:tag>.
378 let param = value.unwrap();
379 let mut components = param.splitn(2, ':');
Zach Reizner55a9e502018-10-03 10:22:32 -0700380 let src =
381 PathBuf::from(
382 components
383 .next()
384 .ok_or_else(|| argument::Error::InvalidValue {
385 value: param.to_owned(),
386 expected: "missing source path for `shared-dir`",
387 })?,
388 );
389 let tag = components
390 .next()
391 .ok_or_else(|| argument::Error::InvalidValue {
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700392 value: param.to_owned(),
393 expected: "missing tag for `shared-dir`",
David Tolnay2bac1e72018-12-12 14:33:42 -0800394 })?
395 .to_owned();
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700396
397 if !src.is_dir() {
398 return Err(argument::Error::InvalidValue {
399 value: param.to_owned(),
400 expected: "source path for `shared-dir` must be a directory",
401 });
402 }
403
Daniel Verkampaac28132018-10-15 14:58:48 -0700404 cfg.shared_dirs.push((src, tag));
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700405 }
Dylan Reide026ef02017-10-02 19:03:52 -0700406 "seccomp-policy-dir" => {
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700407 // `value` is Some because we are in this match so it's safe to unwrap.
Daniel Verkampaac28132018-10-15 14:58:48 -0700408 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
Zach Reizner55a9e502018-10-03 10:22:32 -0700409 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800410 "plugin" => {
411 if !cfg.kernel_path.as_os_str().is_empty() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700412 return Err(argument::Error::TooManyArguments(
413 "`plugin` can not be used with kernel".to_owned(),
414 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800415 } else if cfg.plugin.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700416 return Err(argument::Error::TooManyArguments(
417 "`plugin` already given".to_owned(),
418 ));
Zach Reizner8864cb02018-01-16 17:59:03 -0800419 }
Zach Reiznercc30d582018-01-23 21:16:42 -0800420 let plugin = PathBuf::from(value.unwrap().to_owned());
421 if plugin.is_relative() {
422 return Err(argument::Error::InvalidValue {
Zach Reizner55a9e502018-10-03 10:22:32 -0700423 value: plugin.to_string_lossy().into_owned(),
424 expected: "the plugin path must be an absolute path",
425 });
Zach Reiznercc30d582018-01-23 21:16:42 -0800426 }
427 cfg.plugin = Some(plugin);
Zach Reizner55a9e502018-10-03 10:22:32 -0700428 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700429 "plugin-root" => {
430 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
Zach Reizner55a9e502018-10-03 10:22:32 -0700431 }
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800432 "plugin-mount" => {
433 let components: Vec<&str> = value.unwrap().split(":").collect();
434 if components.len() != 3 {
435 return Err(argument::Error::InvalidValue {
436 value: value.unwrap().to_owned(),
437 expected:
438 "`plugin-mount` must have exactly 3 components: <src>:<dst>:<writable>",
439 });
440 }
441
442 let src = PathBuf::from(components[0]);
443 if src.is_relative() {
444 return Err(argument::Error::InvalidValue {
445 value: components[0].to_owned(),
446 expected: "the source path for `plugin-mount` must be absolute",
447 });
448 }
449 if !src.exists() {
450 return Err(argument::Error::InvalidValue {
451 value: components[0].to_owned(),
452 expected: "the source path for `plugin-mount` does not exist",
453 });
454 }
455
456 let dst = PathBuf::from(components[1]);
457 if dst.is_relative() {
458 return Err(argument::Error::InvalidValue {
459 value: components[1].to_owned(),
460 expected: "the destination path for `plugin-mount` must be absolute",
461 });
462 }
463
464 let writable: bool =
465 components[2]
466 .parse()
467 .map_err(|_| argument::Error::InvalidValue {
468 value: components[2].to_owned(),
469 expected: "the <writable> component for `plugin-mount` is not valid bool",
470 })?;
471
472 cfg.plugin_mounts.push(BindMount { src, dst, writable });
473 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700474 "vhost-net" => cfg.vhost_net = true,
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700475 "tap-fd" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700476 if cfg.tap_fd.is_some() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700477 return Err(argument::Error::TooManyArguments(
478 "`tap-fd` alread given".to_owned(),
479 ));
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700480 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700481 cfg.tap_fd =
Zach Reizner55a9e502018-10-03 10:22:32 -0700482 Some(
483 value
484 .unwrap()
485 .parse()
486 .map_err(|_| argument::Error::InvalidValue {
487 value: value.unwrap().to_owned(),
488 expected: "this value for `tap-fd` must be an unsigned integer",
489 })?,
490 );
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700491 }
Zach Reizner3a8100a2017-09-13 19:15:43 -0700492 "gpu" => {
Daniel Verkampaac28132018-10-15 14:58:48 -0700493 cfg.gpu = true;
Zach Reizner3a8100a2017-09-13 19:15:43 -0700494 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700495 "help" => return Err(argument::Error::PrintHelp),
496 _ => unreachable!(),
497 }
498 Ok(())
499}
500
Dylan Reidbfba9932018-02-05 15:51:59 -0800501fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -0700502 let arguments =
503 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
Tristan Muntsinger4133b012018-12-21 16:01:56 -0800504 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700505 Argument::short_value('p',
506 "params",
507 "PARAMS",
Zach Reiznerbb678712018-01-30 18:13:04 -0800508 "Extra kernel or plugin command line arguments. Can be given more than once."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700509 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
510 Argument::short_value('m',
511 "mem",
512 "N",
513 "Amount of guest memory in MiB. (default: 256)"),
514 Argument::short_value('r',
515 "root",
516 "PATH",
517 "Path to a root disk image. Like `--disk` but adds appropriate kernel command line option."),
518 Argument::short_value('d', "disk", "PATH", "Path to a disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700519 Argument::value("qcow", "PATH", "Path to a qcow2 disk image. (Deprecated; use --disk instead.)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700520 Argument::value("rwdisk", "PATH", "Path to a writable disk image."),
Daniel Verkampf02fdd12018-10-10 17:25:14 -0700521 Argument::value("rwqcow", "PATH", "Path to a writable qcow2 disk image. (Deprecated; use --rwdisk instead.)"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700522 Argument::value("host_ip",
523 "IP",
524 "IP address to assign to host tap interface."),
525 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
526 Argument::value("mac", "MAC", "MAC address for VM."),
paulhsiaf052cfe2019-01-22 15:22:25 +0800527 Argument::flag("cras-audio", "Add an audio device to the VM that plays samples through CRAS server"),
Dylan Reid3082e8e2019-01-07 10:33:48 -0800528 Argument::flag("null-audio", "Add an audio device to the VM that plays samples to /dev/null"),
Stephen Barber28a5a612017-10-20 17:15:30 -0700529 Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
David Reveman52ba4e52018-04-22 21:42:09 -0400530 #[cfg(feature = "wl-dmabuf")]
531 Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
Zach Reiznerefe95782017-08-26 18:05:48 -0700532 Argument::short_value('s',
533 "socket",
534 "PATH",
535 "Path to put the control socket. If PATH is a directory, a name will be generated."),
Dylan Reidd0c9adc2017-10-02 19:04:50 -0700536 Argument::short_flag('u', "multiprocess", "Run each device in a child process(default)."),
537 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
Chirantan Ekboteebd56812018-04-16 19:32:04 -0700538 Argument::value("cid", "CID", "Context ID for virtual sockets."),
539 Argument::value("shared-dir", "PATH:TAG",
540 "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 -0700541 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
Zach Reizner8864cb02018-01-16 17:59:03 -0800542 #[cfg(feature = "plugin")]
Zach Reiznercc30d582018-01-23 21:16:42 -0800543 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
Daniel Verkampbd1a0842019-01-08 15:50:34 -0800544 #[cfg(feature = "plugin")]
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700545 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 -0800546 #[cfg(feature = "plugin")]
Chirantan Ekboted41d7262018-11-16 16:37:45 -0800547 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 +0000548 Argument::flag("vhost-net", "Use vhost for networking."),
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700549 Argument::value("tap-fd",
550 "fd",
551 "File descriptor for configured tap device. Mutually exclusive with `host_ip`, `netmask`, and `mac`."),
Zach Reizner3a8100a2017-09-13 19:15:43 -0700552 #[cfg(feature = "gpu")]
553 Argument::flag("gpu", "(EXPERIMENTAL) enable virtio-gpu device"),
Zach Reiznerefe95782017-08-26 18:05:48 -0700554 Argument::short_flag('h', "help", "Print help message.")];
555
556 let mut cfg = Config::default();
Zach Reizner55a9e502018-10-03 10:22:32 -0700557 let match_res = set_arguments(args, &arguments[..], |name, value| {
558 set_argument(&mut cfg, name, value)
David Tolnay2bac1e72018-12-12 14:33:42 -0800559 })
560 .and_then(|_| {
Zach Reizner8864cb02018-01-16 17:59:03 -0800561 if cfg.kernel_path.as_os_str().is_empty() && cfg.plugin.is_none() {
Zach Reiznerefe95782017-08-26 18:05:48 -0700562 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
563 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700564 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
565 if cfg.host_ip.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700566 return Err(argument::Error::ExpectedArgument(
567 "`host_ip` missing from network config".to_owned(),
568 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700569 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700570 if cfg.netmask.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700571 return Err(argument::Error::ExpectedArgument(
572 "`netmask` missing from network config".to_owned(),
573 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700574 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700575 if cfg.mac_address.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700576 return Err(argument::Error::ExpectedArgument(
577 "`mac` missing from network config".to_owned(),
578 ));
Zach Reiznerefe95782017-08-26 18:05:48 -0700579 }
580 }
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700581 if cfg.plugin_root.is_some() && cfg.plugin.is_none() {
Zach Reizner55a9e502018-10-03 10:22:32 -0700582 return Err(argument::Error::ExpectedArgument(
583 "`plugin-root` requires `plugin`".to_owned(),
584 ));
Dmitry Torokhov0f1770d2018-05-11 10:57:16 -0700585 }
Daniel Verkampaac28132018-10-15 14:58:48 -0700586 if cfg.tap_fd.is_some()
587 && (cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some())
Zach Reizner55a9e502018-10-03 10:22:32 -0700588 {
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700589 return Err(argument::Error::TooManyArguments(
Zach Reizner55a9e502018-10-03 10:22:32 -0700590 "`tap_fd` and any of `host_ip`, `netmask`, or `mac` are mutually exclusive"
591 .to_owned(),
592 ));
Chirantan Ekbote5f787212018-05-31 15:31:31 -0700593 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700594 Ok(())
595 });
596
597 match match_res {
Zach Reizner8864cb02018-01-16 17:59:03 -0800598 #[cfg(feature = "plugin")]
David Tolnay2bac1e72018-12-12 14:33:42 -0800599 Ok(()) if cfg.plugin.is_some() => match plugin::run_config(cfg) {
600 Ok(_) => {
601 info!("crosvm and plugin have exited normally");
602 Ok(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800603 }
David Tolnay2bac1e72018-12-12 14:33:42 -0800604 Err(e) => {
605 error!("{}", e);
606 Err(())
607 }
608 },
Zach Reizner55a9e502018-10-03 10:22:32 -0700609 Ok(()) => match linux::run_config(cfg) {
610 Ok(_) => {
611 info!("crosvm has exited normally");
612 Ok(())
Zach Reiznerefe95782017-08-26 18:05:48 -0700613 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700614 Err(e) => {
615 error!("{}", e);
616 Err(())
617 }
618 },
Dylan Reidbfba9932018-02-05 15:51:59 -0800619 Err(argument::Error::PrintHelp) => {
620 print_help("crosvm run", "KERNEL", &arguments[..]);
621 Ok(())
622 }
Zach Reizner8864cb02018-01-16 17:59:03 -0800623 Err(e) => {
624 println!("{}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -0800625 Err(())
Zach Reizner8864cb02018-01-16 17:59:03 -0800626 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700627 }
628}
629
Zach Reizner6a8fdd92019-01-16 14:38:41 -0800630fn vms_request(
631 cmd_name: &str,
632 cmd_help: &str,
633 request: &VmRequest,
634 args: std::env::Args,
635) -> std::result::Result<(), ()> {
Zach Reiznerefe95782017-08-26 18:05:48 -0700636 if args.len() == 0 {
Zach Reizner6a8fdd92019-01-16 14:38:41 -0800637 print_help(cmd_name, "VM_SOCKET...", &[]);
638 println!("{}", cmd_help);
Zach Reiznerefe95782017-08-26 18:05:48 -0700639 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800640
641 let mut return_result = Ok(());
Zach Reiznerefe95782017-08-26 18:05:48 -0700642 for socket_path in args {
643 match UnixDatagram::unbound().and_then(|s| {
Zach Reizner55a9e502018-10-03 10:22:32 -0700644 s.connect(&socket_path)?;
645 Ok(s)
646 }) {
Zach Reiznerefe95782017-08-26 18:05:48 -0700647 Ok(s) => {
Jingkui Wange13b1802018-10-03 13:04:47 -0700648 let sender = Sender::<VmRequest>::new(s);
Zach Reizner6a8fdd92019-01-16 14:38:41 -0800649 if let Err(e) = sender.send(request) {
Zach Reizner55a9e502018-10-03 10:22:32 -0700650 error!(
Zach Reizner6a8fdd92019-01-16 14:38:41 -0800651 "failed to send request to socket at '{}': {:?}",
Zach Reizner55a9e502018-10-03 10:22:32 -0700652 socket_path, e
653 );
Zach Reiznerefe95782017-08-26 18:05:48 -0700654 }
655 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800656 Err(e) => {
657 error!("failed to connect to socket at '{}': {}", socket_path, e);
658 return_result = Err(());;
659 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700660 }
661 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800662
663 return_result
Zach Reiznerefe95782017-08-26 18:05:48 -0700664}
665
Zach Reizner6a8fdd92019-01-16 14:38:41 -0800666fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> {
667 vms_request(
668 "crosvm stop",
669 "Stops the crosvm instance listening on each `VM_SOCKET` given.",
670 &VmRequest::Exit,
671 args,
672 )
673}
674
675fn suspend_vms(args: std::env::Args) -> std::result::Result<(), ()> {
676 vms_request(
677 "crosvm suspend",
678 "Suspends the crosvm instance listening on each `VM_SOCKET` given.",
679 &VmRequest::Suspend,
680 args,
681 )
682}
683
684fn resume_vms(args: std::env::Args) -> std::result::Result<(), ()> {
685 vms_request(
686 "crosvm resume",
687 "Suspends the crosvm instance listening on each `VM_SOCKET` given.",
688 &VmRequest::Resume,
689 args,
690 )
691}
692
Dylan Reidbfba9932018-02-05 15:51:59 -0800693fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
Dylan Reidd4432042017-12-06 18:20:09 -0800694 if args.len() < 2 {
Daniel Verkampf76869d2019-01-03 12:53:04 -0800695 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
696 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
Dylan Reidd4432042017-12-06 18:20:09 -0800697 }
Daniel Verkampf76869d2019-01-03 12:53:04 -0800698 let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
Dylan Reidd4432042017-12-06 18:20:09 -0800699 Ok(n) => n,
700 Err(_) => {
Daniel Verkampf76869d2019-01-03 12:53:04 -0800701 error!("Failed to parse number of bytes");
Dylan Reidbfba9932018-02-05 15:51:59 -0800702 return Err(());
Zach Reizner55a9e502018-10-03 10:22:32 -0700703 }
Dylan Reidd4432042017-12-06 18:20:09 -0800704 };
705
Dylan Reidbfba9932018-02-05 15:51:59 -0800706 let mut return_result = Ok(());
Dylan Reidd4432042017-12-06 18:20:09 -0800707 for socket_path in args {
708 match UnixDatagram::unbound().and_then(|s| {
Zach Reizner55a9e502018-10-03 10:22:32 -0700709 s.connect(&socket_path)?;
710 Ok(s)
711 }) {
Dylan Reidd4432042017-12-06 18:20:09 -0800712 Ok(s) => {
Jingkui Wange13b1802018-10-03 13:04:47 -0700713 let sender = Sender::<VmRequest>::new(s);
Daniel Verkampf76869d2019-01-03 12:53:04 -0800714 if let Err(e) = sender.send(&VmRequest::BalloonAdjust(num_bytes)) {
Zach Reizner55a9e502018-10-03 10:22:32 -0700715 error!(
716 "failed to send balloon request to socket at '{}': {:?}",
717 socket_path, e
718 );
Dylan Reidd4432042017-12-06 18:20:09 -0800719 }
720 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800721 Err(e) => {
722 error!("failed to connect to socket at '{}': {}", socket_path, e);
723 return_result = Err(());
724 }
Dylan Reidd4432042017-12-06 18:20:09 -0800725 }
726 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800727
728 return_result
Dylan Reidd4432042017-12-06 18:20:09 -0800729}
Zach Reiznerefe95782017-08-26 18:05:48 -0700730
Dylan Reid2dcb6322018-07-13 10:42:48 -0700731fn create_qcow2(mut args: std::env::Args) -> std::result::Result<(), ()> {
732 if args.len() != 2 {
733 print_help("crosvm create_qcow2", "PATH SIZE", &[]);
Dylan Reid940259c2018-07-20 14:22:33 -0700734 println!("Create a new QCOW2 image at `PATH` of the specified `SIZE` in bytes.");
Dylan Reid2dcb6322018-07-13 10:42:48 -0700735 }
736 let file_path = args.nth(0).unwrap();
737 let size: u64 = match args.nth(0).unwrap().parse::<u64>() {
738 Ok(n) => n,
739 Err(_) => {
740 error!("Failed to parse size of the disk.");
741 return Err(());
Zach Reizner55a9e502018-10-03 10:22:32 -0700742 }
Dylan Reid2dcb6322018-07-13 10:42:48 -0700743 };
744
745 let file = OpenOptions::new()
746 .create(true)
747 .read(true)
748 .write(true)
749 .open(&file_path)
750 .map_err(|e| {
751 error!("Failed opening qcow file at '{}': {:?}", file_path, e);
Dylan Reid2dcb6322018-07-13 10:42:48 -0700752 })?;
753
Zach Reizner55a9e502018-10-03 10:22:32 -0700754 QcowFile::new(file, size).map_err(|e| {
755 error!("Failed to create qcow file at '{}': {:?}", file_path, e);
Zach Reizner55a9e502018-10-03 10:22:32 -0700756 })?;
Dylan Reid2dcb6322018-07-13 10:42:48 -0700757
758 Ok(())
759}
760
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800761fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
762 if args.len() < 2 {
763 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
764 println!("Manage attached virtual disk devices.");
765 println!("Subcommands:");
766 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
767 }
768 let subcommand: &str = &args.nth(0).unwrap();
769
770 let request = match subcommand {
771 "resize" => {
772 let disk_index = match args.nth(0).unwrap().parse::<usize>() {
773 Ok(n) => n,
774 Err(_) => {
775 error!("Failed to parse disk index");
776 return Err(());
777 }
778 };
779
780 let new_size = match args.nth(0).unwrap().parse::<u64>() {
781 Ok(n) => n,
782 Err(_) => {
783 error!("Failed to parse disk size");
784 return Err(());
785 }
786 };
787
788 VmRequest::DiskResize {
789 disk_index,
790 new_size,
791 }
792 }
793 _ => {
794 error!("Unknown disk subcommand '{}'", subcommand);
795 return Err(());
796 }
797 };
798
799 let mut return_result = Ok(());
800 for socket_path in args {
801 match UnixDatagram::unbound().and_then(|s| {
802 s.connect(&socket_path)?;
803 Ok(s)
804 }) {
805 Ok(s) => {
806 let sender = Sender::<VmRequest>::new(s);
807 if let Err(e) = sender.send(&request) {
808 error!(
809 "failed to send disk request to socket at '{}': {:?}",
810 socket_path, e
811 );
812 }
813 }
814 Err(e) => {
815 error!("failed to connect to socket at '{}': {}", socket_path, e);
816 return_result = Err(());
817 }
818 }
819 }
820
821 return_result
822}
823
Zach Reiznerefe95782017-08-26 18:05:48 -0700824fn print_usage() {
825 print_help("crosvm", "[stop|run]", &[]);
826 println!("Commands:");
827 println!(" stop - Stops crosvm instances via their control sockets.");
828 println!(" run - Start a new crosvm instance.");
Dylan Reid2dcb6322018-07-13 10:42:48 -0700829 println!(" create_qcow2 - Create a new qcow2 disk image file.");
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800830 println!(" disk - Manage attached virtual disk devices.")
Zach Reiznerefe95782017-08-26 18:05:48 -0700831}
832
Dylan Reidbfba9932018-02-05 15:51:59 -0800833fn crosvm_main() -> std::result::Result<(), ()> {
Stephen Barber56fbf092017-06-29 16:12:14 -0700834 if let Err(e) = syslog::init() {
Zach Reiznera1422e62018-09-20 14:41:40 -0700835 println!("failed to initialize syslog: {:?}", e);
Dylan Reidbfba9932018-02-05 15:51:59 -0800836 return Err(());
Stephen Barber56fbf092017-06-29 16:12:14 -0700837 }
Zach Reizner639d9672017-05-01 17:57:18 -0700838
Zach Reiznerefe95782017-08-26 18:05:48 -0700839 let mut args = std::env::args();
840 if args.next().is_none() {
841 error!("expected executable name");
Dylan Reidbfba9932018-02-05 15:51:59 -0800842 return Err(());
Zach Reizner639d9672017-05-01 17:57:18 -0700843 }
Zach Reiznerefe95782017-08-26 18:05:48 -0700844
Zach Reizner8864cb02018-01-16 17:59:03 -0800845 // Past this point, usage of exit is in danger of leaking zombie processes.
Dylan Reidbfba9932018-02-05 15:51:59 -0800846 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
847 None => {
848 print_usage();
849 Ok(())
850 }
Zach Reizner55a9e502018-10-03 10:22:32 -0700851 Some("stop") => stop_vms(args),
Zach Reizner6a8fdd92019-01-16 14:38:41 -0800852 Some("suspend") => suspend_vms(args),
853 Some("resume") => resume_vms(args),
Zach Reizner55a9e502018-10-03 10:22:32 -0700854 Some("run") => run_vm(args),
855 Some("balloon") => balloon_vms(args),
856 Some("create_qcow2") => create_qcow2(args),
Daniel Verkamp92f73d72018-12-04 13:17:46 -0800857 Some("disk") => disk_cmd(args),
Zach Reiznerefe95782017-08-26 18:05:48 -0700858 Some(c) => {
859 println!("invalid subcommand: {:?}", c);
860 print_usage();
Dylan Reidbfba9932018-02-05 15:51:59 -0800861 Err(())
Zach Reiznerefe95782017-08-26 18:05:48 -0700862 }
Dylan Reidbfba9932018-02-05 15:51:59 -0800863 };
Zach Reiznerefe95782017-08-26 18:05:48 -0700864
865 // Reap exit status from any child device processes. At this point, all devices should have been
866 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
867 // take some time for the processes to shut down.
868 if !wait_all_children() {
869 // We gave them a chance, and it's too late.
870 warn!("not all child processes have exited; sending SIGKILL");
871 if let Err(e) = kill_process_group() {
872 // We're now at the mercy of the OS to clean up after us.
873 warn!("unable to kill all child processes: {:?}", e);
874 }
875 }
876
877 // WARNING: Any code added after this point is not guaranteed to run
878 // since we may forcibly kill this process (and its children) above.
Dylan Reidbfba9932018-02-05 15:51:59 -0800879 ret
880}
881
882fn main() {
883 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
Zach Reizner639d9672017-05-01 17:57:18 -0700884}