1 // 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
7 pub mod argument;
8 pub mod linux;
9 pub mod panic_hook;
10 #[cfg(feature = "plugin")]
11 pub mod plugin;
12
13 use std::collections::BTreeMap;
14 use std::fmt;
15 use std::fs::{File, OpenOptions};
16 use std::net;
17 use std::num::ParseIntError;
18 use std::os::unix::io::{FromRawFd, RawFd};
19 use std::path::{Path, PathBuf};
20 use std::string::String;
21 use std::thread::sleep;
22 use std::time::Duration;
23
24 use devices::{SerialParameters, SerialType};
25 use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
26 use qcow::QcowFile;
27 use sys_util::{
28 debug, error, getpid, info, kill_process_group, net::UnixSeqpacket, reap_child, syslog,
29 validate_raw_fd, warn,
30 };
31 use vm_control::{
32 BalloonControlCommand, DiskControlCommand, MaybeOwnedFd, UsbControlCommand, UsbControlResult,
33 VmControlRequestSocket, VmRequest, VmResponse, USB_CONTROL_MAX_PORTS,
34 };
35
36 use crate::argument::{print_help, set_arguments, Argument};
37
38 static SECCOMP_POLICY_DIR: &'static str = "/usr/share/policy/crosvm";
39
40 struct DiskOption {
41 path: PathBuf,
42 read_only: bool,
43 }
44
45 #[allow(dead_code)]
46 struct BindMount {
47 src: PathBuf,
48 dst: PathBuf,
49 writable: bool,
50 }
51
52 #[allow(dead_code)]
53 struct GidMap {
54 inner: libc::gid_t,
55 outer: libc::gid_t,
56 count: u32,
57 }
58
59 const DEFAULT_TOUCH_DEVICE_WIDTH: u32 = 800;
60 const DEFAULT_TOUCH_DEVICE_HEIGHT: u32 = 1280;
61
62 struct TouchDeviceOption {
63 path: PathBuf,
64 width: u32,
65 height: u32,
66 }
67
68 impl TouchDeviceOption {
new(path: PathBuf) -> TouchDeviceOption69 fn new(path: PathBuf) -> TouchDeviceOption {
70 TouchDeviceOption {
71 path,
72 width: DEFAULT_TOUCH_DEVICE_WIDTH,
73 height: DEFAULT_TOUCH_DEVICE_HEIGHT,
74 }
75 }
76 }
77
78 #[derive(Debug)]
79 pub enum Executable {
80 Bios(PathBuf),
81 Kernel(PathBuf),
82 Plugin(PathBuf),
83 }
84
executable_is_plugin(executable: &Option<Executable>) -> bool85 fn executable_is_plugin(executable: &Option<Executable>) -> bool {
86 match executable {
87 Some(Executable::Plugin(_)) => true,
88 _ => false,
89 }
90 }
91
92 pub struct Config {
93 vcpu_count: Option<u32>,
94 vcpu_affinity: Vec<usize>,
95 memory: Option<usize>,
96 executable_path: Option<Executable>,
97 android_fstab: Option<PathBuf>,
98 initrd_path: Option<PathBuf>,
99 params: Vec<String>,
100 socket_path: Option<PathBuf>,
101 plugin_root: Option<PathBuf>,
102 plugin_mounts: Vec<BindMount>,
103 plugin_gid_maps: Vec<GidMap>,
104 disks: Vec<DiskOption>,
105 pmem_devices: Vec<DiskOption>,
106 host_ip: Option<net::Ipv4Addr>,
107 netmask: Option<net::Ipv4Addr>,
108 mac_address: Option<net_util::MacAddress>,
109 vhost_net: bool,
110 tap_fd: Vec<RawFd>,
111 cid: Option<u64>,
112 wayland_socket_path: Option<PathBuf>,
113 wayland_dmabuf: bool,
114 shared_dirs: Vec<(PathBuf, String)>,
115 sandbox: bool,
116 seccomp_policy_dir: PathBuf,
117 gpu: bool,
118 software_tpm: bool,
119 cras_audio: bool,
120 cras_capture: bool,
121 null_audio: bool,
122 serial_parameters: BTreeMap<u8, SerialParameters>,
123 syslog_tag: Option<String>,
124 virtio_single_touch: Option<TouchDeviceOption>,
125 virtio_trackpad: Option<TouchDeviceOption>,
126 virtio_mouse: Option<PathBuf>,
127 virtio_keyboard: Option<PathBuf>,
128 virtio_input_evdevs: Vec<PathBuf>,
129 split_irqchip: bool,
130 }
131
132 impl Default for Config {
default() -> Config133 fn default() -> Config {
134 Config {
135 vcpu_count: None,
136 vcpu_affinity: Vec::new(),
137 memory: None,
138 executable_path: None,
139 android_fstab: None,
140 initrd_path: None,
141 params: Vec::new(),
142 socket_path: None,
143 plugin_root: None,
144 plugin_mounts: Vec::new(),
145 plugin_gid_maps: Vec::new(),
146 disks: Vec::new(),
147 pmem_devices: Vec::new(),
148 host_ip: None,
149 netmask: None,
150 mac_address: None,
151 vhost_net: false,
152 tap_fd: Vec::new(),
153 cid: None,
154 gpu: false,
155 software_tpm: false,
156 wayland_socket_path: None,
157 wayland_dmabuf: false,
158 shared_dirs: Vec::new(),
159 sandbox: !cfg!(feature = "default-no-sandbox"),
160 seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
161 cras_audio: false,
162 cras_capture: false,
163 null_audio: false,
164 serial_parameters: BTreeMap::new(),
165 syslog_tag: None,
166 virtio_single_touch: None,
167 virtio_trackpad: None,
168 virtio_mouse: None,
169 virtio_keyboard: None,
170 virtio_input_evdevs: Vec::new(),
171 split_irqchip: false,
172 }
173 }
174 }
175
176 // Wait for all children to exit. Return true if they have all exited, false
177 // otherwise.
wait_all_children() -> bool178 fn wait_all_children() -> bool {
179 const CHILD_WAIT_MAX_ITER: isize = 100;
180 const CHILD_WAIT_MS: u64 = 10;
181 for _ in 0..CHILD_WAIT_MAX_ITER {
182 loop {
183 match reap_child() {
184 Ok(0) => break,
185 // We expect ECHILD which indicates that there were no children left.
186 Err(e) if e.errno() == libc::ECHILD => return true,
187 Err(e) => {
188 warn!("error while waiting for children: {}", e);
189 return false;
190 }
191 // We reaped one child, so continue reaping.
192 _ => {}
193 }
194 }
195 // There's no timeout option for waitpid which reap_child calls internally, so our only
196 // recourse is to sleep while waiting for the children to exit.
197 sleep(Duration::from_millis(CHILD_WAIT_MS));
198 }
199
200 // If we've made it to this point, not all of the children have exited.
201 false
202 }
203
204 /// Parse a comma-separated list of CPU numbers and ranges and convert it to a Vec of CPU numbers.
parse_cpu_set(s: &str) -> argument::Result<Vec<usize>>205 fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
206 let mut cpuset = Vec::new();
207 for part in s.split(',') {
208 let range: Vec<&str> = part.split('-').collect();
209 if range.len() == 0 || range.len() > 2 {
210 return Err(argument::Error::InvalidValue {
211 value: part.to_owned(),
212 expected: "invalid list syntax",
213 });
214 }
215 let first_cpu: usize = range[0]
216 .parse()
217 .map_err(|_| argument::Error::InvalidValue {
218 value: part.to_owned(),
219 expected: "CPU index must be a non-negative integer",
220 })?;
221 let last_cpu: usize = if range.len() == 2 {
222 range[1]
223 .parse()
224 .map_err(|_| argument::Error::InvalidValue {
225 value: part.to_owned(),
226 expected: "CPU index must be a non-negative integer",
227 })?
228 } else {
229 first_cpu
230 };
231
232 if last_cpu < first_cpu {
233 return Err(argument::Error::InvalidValue {
234 value: part.to_owned(),
235 expected: "CPU ranges must be from low to high",
236 });
237 }
238
239 for cpu in first_cpu..=last_cpu {
240 cpuset.push(cpu);
241 }
242 }
243 Ok(cpuset)
244 }
245
parse_serial_options(s: &str) -> argument::Result<SerialParameters>246 fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
247 let mut serial_setting = SerialParameters {
248 type_: SerialType::Sink,
249 path: None,
250 num: 1,
251 console: false,
252 };
253
254 let opts = s
255 .split(",")
256 .map(|frag| frag.split("="))
257 .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
258
259 for (k, v) in opts {
260 match k {
261 "type" => {
262 serial_setting.type_ = v
263 .parse::<SerialType>()
264 .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
265 }
266 "num" => {
267 let num = v.parse::<u8>().map_err(|e| {
268 argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
269 })?;
270 if num < 1 || num > 4 {
271 return Err(argument::Error::InvalidValue {
272 value: num.to_string(),
273 expected: "Serial port num must be between 1 - 4",
274 });
275 }
276 serial_setting.num = num;
277 }
278 "console" => {
279 serial_setting.console = v.parse::<bool>().map_err(|e| {
280 argument::Error::Syntax(format!(
281 "serial device console is not parseable: {}",
282 e
283 ))
284 })?
285 }
286 "path" => serial_setting.path = Some(PathBuf::from(v)),
287 _ => {
288 return Err(argument::Error::UnknownArgument(format!(
289 "serial parameter {}",
290 k
291 )));
292 }
293 }
294 }
295
296 Ok(serial_setting)
297 }
298
set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()>299 fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
300 match name {
301 "" => {
302 if cfg.executable_path.is_some() {
303 return Err(argument::Error::TooManyArguments(format!(
304 "A VM executable was already specified: {:?}",
305 cfg.executable_path
306 )));
307 }
308 let kernel_path = PathBuf::from(value.unwrap());
309 if !kernel_path.exists() {
310 return Err(argument::Error::InvalidValue {
311 value: value.unwrap().to_owned(),
312 expected: "this kernel path does not exist",
313 });
314 }
315 cfg.executable_path = Some(Executable::Kernel(kernel_path));
316 }
317 "android-fstab" => {
318 if cfg.android_fstab.is_some()
319 && !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
320 {
321 return Err(argument::Error::TooManyArguments(
322 "expected exactly one android fstab path".to_owned(),
323 ));
324 } else {
325 let android_fstab = PathBuf::from(value.unwrap());
326 if !android_fstab.exists() {
327 return Err(argument::Error::InvalidValue {
328 value: value.unwrap().to_owned(),
329 expected: "this android fstab path does not exist",
330 });
331 }
332 cfg.android_fstab = Some(android_fstab);
333 }
334 }
335 "params" => {
336 cfg.params.push(value.unwrap().to_owned());
337 }
338 "cpus" => {
339 if cfg.vcpu_count.is_some() {
340 return Err(argument::Error::TooManyArguments(
341 "`cpus` already given".to_owned(),
342 ));
343 }
344 cfg.vcpu_count =
345 Some(
346 value
347 .unwrap()
348 .parse()
349 .map_err(|_| argument::Error::InvalidValue {
350 value: value.unwrap().to_owned(),
351 expected: "this value for `cpus` needs to be integer",
352 })?,
353 )
354 }
355 "cpu-affinity" => {
356 if cfg.vcpu_affinity.len() != 0 {
357 return Err(argument::Error::TooManyArguments(
358 "`cpu-affinity` already given".to_owned(),
359 ));
360 }
361 cfg.vcpu_affinity = parse_cpu_set(value.unwrap())?;
362 }
363 "mem" => {
364 if cfg.memory.is_some() {
365 return Err(argument::Error::TooManyArguments(
366 "`mem` already given".to_owned(),
367 ));
368 }
369 cfg.memory =
370 Some(
371 value
372 .unwrap()
373 .parse()
374 .map_err(|_| argument::Error::InvalidValue {
375 value: value.unwrap().to_owned(),
376 expected: "this value for `mem` needs to be integer",
377 })?,
378 )
379 }
380 "cras-audio" => {
381 cfg.cras_audio = true;
382 }
383 "cras-capture" => {
384 cfg.cras_capture = true;
385 }
386 "null-audio" => {
387 cfg.null_audio = true;
388 }
389 "serial" => {
390 let serial_params = parse_serial_options(value.unwrap())?;
391 let num = serial_params.num;
392 if cfg.serial_parameters.contains_key(&num) {
393 return Err(argument::Error::TooManyArguments(format!(
394 "serial num {}",
395 num
396 )));
397 }
398
399 if serial_params.console {
400 for params in cfg.serial_parameters.values() {
401 if params.console {
402 return Err(argument::Error::TooManyArguments(format!(
403 "serial device {} already set as console",
404 params.num
405 )));
406 }
407 }
408 }
409
410 cfg.serial_parameters.insert(num, serial_params);
411 }
412 "syslog-tag" => {
413 if cfg.syslog_tag.is_some() {
414 return Err(argument::Error::TooManyArguments(
415 "`syslog-tag` already given".to_owned(),
416 ));
417 }
418 syslog::set_proc_name(value.unwrap());
419 cfg.syslog_tag = Some(value.unwrap().to_owned());
420 }
421 "root" | "disk" | "rwdisk" | "qcow" | "rwqcow" => {
422 let disk_path = PathBuf::from(value.unwrap());
423 if !disk_path.exists() {
424 return Err(argument::Error::InvalidValue {
425 value: value.unwrap().to_owned(),
426 expected: "this disk path does not exist",
427 });
428 }
429 if name == "root" {
430 if cfg.disks.len() >= 26 {
431 return Err(argument::Error::TooManyArguments(
432 "ran out of letters for to assign to root disk".to_owned(),
433 ));
434 }
435 cfg.params.push(format!(
436 "root=/dev/vd{} ro",
437 char::from(b'a' + cfg.disks.len() as u8)
438 ));
439 }
440 cfg.disks.push(DiskOption {
441 path: disk_path,
442 read_only: !name.starts_with("rw"),
443 });
444 }
445 "pmem-device" | "rw-pmem-device" => {
446 let disk_path = PathBuf::from(value.unwrap());
447 if !disk_path.exists() {
448 return Err(argument::Error::InvalidValue {
449 value: value.unwrap().to_owned(),
450 expected: "this disk path does not exist",
451 });
452 }
453
454 cfg.pmem_devices.push(DiskOption {
455 path: disk_path,
456 read_only: !name.starts_with("rw"),
457 });
458 }
459 "host_ip" => {
460 if cfg.host_ip.is_some() {
461 return Err(argument::Error::TooManyArguments(
462 "`host_ip` already given".to_owned(),
463 ));
464 }
465 cfg.host_ip =
466 Some(
467 value
468 .unwrap()
469 .parse()
470 .map_err(|_| argument::Error::InvalidValue {
471 value: value.unwrap().to_owned(),
472 expected: "`host_ip` needs to be in the form \"x.x.x.x\"",
473 })?,
474 )
475 }
476 "netmask" => {
477 if cfg.netmask.is_some() {
478 return Err(argument::Error::TooManyArguments(
479 "`netmask` already given".to_owned(),
480 ));
481 }
482 cfg.netmask =
483 Some(
484 value
485 .unwrap()
486 .parse()
487 .map_err(|_| argument::Error::InvalidValue {
488 value: value.unwrap().to_owned(),
489 expected: "`netmask` needs to be in the form \"x.x.x.x\"",
490 })?,
491 )
492 }
493 "mac" => {
494 if cfg.mac_address.is_some() {
495 return Err(argument::Error::TooManyArguments(
496 "`mac` already given".to_owned(),
497 ));
498 }
499 cfg.mac_address =
500 Some(
501 value
502 .unwrap()
503 .parse()
504 .map_err(|_| argument::Error::InvalidValue {
505 value: value.unwrap().to_owned(),
506 expected: "`mac` needs to be in the form \"XX:XX:XX:XX:XX:XX\"",
507 })?,
508 )
509 }
510 "wayland-sock" => {
511 if cfg.wayland_socket_path.is_some() {
512 return Err(argument::Error::TooManyArguments(
513 "`wayland-sock` already given".to_owned(),
514 ));
515 }
516 let wayland_socket_path = PathBuf::from(value.unwrap());
517 if !wayland_socket_path.exists() {
518 return Err(argument::Error::InvalidValue {
519 value: value.unwrap().to_string(),
520 expected: "Wayland socket does not exist",
521 });
522 }
523 cfg.wayland_socket_path = Some(wayland_socket_path);
524 }
525 #[cfg(feature = "wl-dmabuf")]
526 "wayland-dmabuf" => cfg.wayland_dmabuf = true,
527 "socket" => {
528 if cfg.socket_path.is_some() {
529 return Err(argument::Error::TooManyArguments(
530 "`socket` already given".to_owned(),
531 ));
532 }
533 let mut socket_path = PathBuf::from(value.unwrap());
534 if socket_path.is_dir() {
535 socket_path.push(format!("crosvm-{}.sock", getpid()));
536 }
537 if socket_path.exists() {
538 return Err(argument::Error::InvalidValue {
539 value: socket_path.to_string_lossy().into_owned(),
540 expected: "this socket path already exists",
541 });
542 }
543 cfg.socket_path = Some(socket_path);
544 }
545 "disable-sandbox" => {
546 cfg.sandbox = false;
547 }
548 "cid" => {
549 if cfg.cid.is_some() {
550 return Err(argument::Error::TooManyArguments(
551 "`cid` alread given".to_owned(),
552 ));
553 }
554 cfg.cid = Some(
555 value
556 .unwrap()
557 .parse()
558 .map_err(|_| argument::Error::InvalidValue {
559 value: value.unwrap().to_owned(),
560 expected: "this value for `cid` must be an unsigned integer",
561 })?,
562 );
563 }
564 "shared-dir" => {
565 // Formatted as <src:tag>.
566 let param = value.unwrap();
567 let mut components = param.splitn(2, ':');
568 let src =
569 PathBuf::from(
570 components
571 .next()
572 .ok_or_else(|| argument::Error::InvalidValue {
573 value: param.to_owned(),
574 expected: "missing source path for `shared-dir`",
575 })?,
576 );
577 let tag = components
578 .next()
579 .ok_or_else(|| argument::Error::InvalidValue {
580 value: param.to_owned(),
581 expected: "missing tag for `shared-dir`",
582 })?
583 .to_owned();
584
585 if !src.is_dir() {
586 return Err(argument::Error::InvalidValue {
587 value: param.to_owned(),
588 expected: "source path for `shared-dir` must be a directory",
589 });
590 }
591
592 cfg.shared_dirs.push((src, tag));
593 }
594 "seccomp-policy-dir" => {
595 // `value` is Some because we are in this match so it's safe to unwrap.
596 cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
597 }
598 "plugin" => {
599 if cfg.executable_path.is_some() {
600 return Err(argument::Error::TooManyArguments(format!(
601 "A VM executable was already specified: {:?}",
602 cfg.executable_path
603 )));
604 }
605 let plugin = PathBuf::from(value.unwrap().to_owned());
606 if plugin.is_relative() {
607 return Err(argument::Error::InvalidValue {
608 value: plugin.to_string_lossy().into_owned(),
609 expected: "the plugin path must be an absolute path",
610 });
611 }
612 cfg.executable_path = Some(Executable::Plugin(plugin));
613 }
614 "plugin-root" => {
615 cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
616 }
617 "plugin-mount" => {
618 let components: Vec<&str> = value.unwrap().split(":").collect();
619 if components.len() != 3 {
620 return Err(argument::Error::InvalidValue {
621 value: value.unwrap().to_owned(),
622 expected:
623 "`plugin-mount` must have exactly 3 components: <src>:<dst>:<writable>",
624 });
625 }
626
627 let src = PathBuf::from(components[0]);
628 if src.is_relative() {
629 return Err(argument::Error::InvalidValue {
630 value: components[0].to_owned(),
631 expected: "the source path for `plugin-mount` must be absolute",
632 });
633 }
634 if !src.exists() {
635 return Err(argument::Error::InvalidValue {
636 value: components[0].to_owned(),
637 expected: "the source path for `plugin-mount` does not exist",
638 });
639 }
640
641 let dst = PathBuf::from(components[1]);
642 if dst.is_relative() {
643 return Err(argument::Error::InvalidValue {
644 value: components[1].to_owned(),
645 expected: "the destination path for `plugin-mount` must be absolute",
646 });
647 }
648
649 let writable: bool =
650 components[2]
651 .parse()
652 .map_err(|_| argument::Error::InvalidValue {
653 value: components[2].to_owned(),
654 expected: "the <writable> component for `plugin-mount` is not valid bool",
655 })?;
656
657 cfg.plugin_mounts.push(BindMount { src, dst, writable });
658 }
659 "plugin-gid-map" => {
660 let components: Vec<&str> = value.unwrap().split(":").collect();
661 if components.len() != 3 {
662 return Err(argument::Error::InvalidValue {
663 value: value.unwrap().to_owned(),
664 expected:
665 "`plugin-gid-map` must have exactly 3 components: <inner>:<outer>:<count>",
666 });
667 }
668
669 let inner: libc::gid_t =
670 components[0]
671 .parse()
672 .map_err(|_| argument::Error::InvalidValue {
673 value: components[0].to_owned(),
674 expected: "the <inner> component for `plugin-gid-map` is not valid gid",
675 })?;
676
677 let outer: libc::gid_t =
678 components[1]
679 .parse()
680 .map_err(|_| argument::Error::InvalidValue {
681 value: components[1].to_owned(),
682 expected: "the <outer> component for `plugin-gid-map` is not valid gid",
683 })?;
684
685 let count: u32 = components[2]
686 .parse()
687 .map_err(|_| argument::Error::InvalidValue {
688 value: components[2].to_owned(),
689 expected: "the <count> component for `plugin-gid-map` is not valid number",
690 })?;
691
692 cfg.plugin_gid_maps.push(GidMap {
693 inner,
694 outer,
695 count,
696 });
697 }
698 "vhost-net" => cfg.vhost_net = true,
699 "tap-fd" => {
700 cfg.tap_fd.push(
701 value
702 .unwrap()
703 .parse()
704 .map_err(|_| argument::Error::InvalidValue {
705 value: value.unwrap().to_owned(),
706 expected: "this value for `tap-fd` must be an unsigned integer",
707 })?,
708 );
709 }
710 "gpu" => {
711 cfg.gpu = true;
712 }
713 "software-tpm" => {
714 cfg.software_tpm = true;
715 }
716 "single-touch" => {
717 if cfg.virtio_single_touch.is_some() {
718 return Err(argument::Error::TooManyArguments(
719 "`single-touch` already given".to_owned(),
720 ));
721 }
722 let mut it = value.unwrap().split(":");
723
724 let mut single_touch_spec =
725 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
726 if let Some(width) = it.next() {
727 single_touch_spec.width = width.trim().parse().unwrap();
728 }
729 if let Some(height) = it.next() {
730 single_touch_spec.height = height.trim().parse().unwrap();
731 }
732
733 cfg.virtio_single_touch = Some(single_touch_spec);
734 }
735 "trackpad" => {
736 if cfg.virtio_trackpad.is_some() {
737 return Err(argument::Error::TooManyArguments(
738 "`trackpad` already given".to_owned(),
739 ));
740 }
741 let mut it = value.unwrap().split(":");
742
743 let mut trackpad_spec =
744 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
745 if let Some(width) = it.next() {
746 trackpad_spec.width = width.trim().parse().unwrap();
747 }
748 if let Some(height) = it.next() {
749 trackpad_spec.height = height.trim().parse().unwrap();
750 }
751
752 cfg.virtio_trackpad = Some(trackpad_spec);
753 }
754 "mouse" => {
755 if cfg.virtio_mouse.is_some() {
756 return Err(argument::Error::TooManyArguments(
757 "`mouse` already given".to_owned(),
758 ));
759 }
760 cfg.virtio_mouse = Some(PathBuf::from(value.unwrap().to_owned()));
761 }
762 "keyboard" => {
763 if cfg.virtio_keyboard.is_some() {
764 return Err(argument::Error::TooManyArguments(
765 "`keyboard` already given".to_owned(),
766 ));
767 }
768 cfg.virtio_keyboard = Some(PathBuf::from(value.unwrap().to_owned()));
769 }
770 "evdev" => {
771 let dev_path = PathBuf::from(value.unwrap());
772 if !dev_path.exists() {
773 return Err(argument::Error::InvalidValue {
774 value: value.unwrap().to_owned(),
775 expected: "this input device path does not exist",
776 });
777 }
778 cfg.virtio_input_evdevs.push(dev_path);
779 }
780 "split-irqchip" => {
781 cfg.split_irqchip = true;
782 }
783 "initrd" => {
784 cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
785 }
786 "bios" => {
787 if cfg.executable_path.is_some() {
788 return Err(argument::Error::TooManyArguments(format!(
789 "A VM executable was already specified: {:?}",
790 cfg.executable_path
791 )));
792 }
793 cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
794 }
795 "help" => return Err(argument::Error::PrintHelp),
796 _ => unreachable!(),
797 }
798 Ok(())
799 }
800
run_vm(args: std::env::Args) -> std::result::Result<(), ()>801 fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
802 let arguments =
803 &[Argument::positional("KERNEL", "bzImage of kernel to run"),
804 Argument::value("android-fstab", "PATH", "Path to Android fstab"),
805 Argument::short_value('i', "initrd", "PATH", "Initial ramdisk to load."),
806 Argument::short_value('p',
807 "params",
808 "PARAMS",
809 "Extra kernel or plugin command line arguments. Can be given more than once."),
810 Argument::short_value('c', "cpus", "N", "Number of VCPUs. (default: 1)"),
811 Argument::value("cpu-affinity", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on. (e.g. 0,1-3,5) (default: no mask)"),
812 Argument::short_value('m',
813 "mem",
814 "N",
815 "Amount of guest memory in MiB. (default: 256)"),
816 Argument::short_value('r',
817 "root",
818 "PATH",
819 "Path to a root disk image. Like `--disk` but adds appropriate kernel command line option."),
820 Argument::short_value('d', "disk", "PATH", "Path to a disk image."),
821 Argument::value("qcow", "PATH", "Path to a qcow2 disk image. (Deprecated; use --disk instead.)"),
822 Argument::value("rwdisk", "PATH", "Path to a writable disk image."),
823 Argument::value("rwqcow", "PATH", "Path to a writable qcow2 disk image. (Deprecated; use --rwdisk instead.)"),
824 Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
825 Argument::value("pmem-device", "PATH", "Path to a disk image."),
826 Argument::value("host_ip",
827 "IP",
828 "IP address to assign to host tap interface."),
829 Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
830 Argument::value("mac", "MAC", "MAC address for VM."),
831 Argument::flag("cras-audio", "Add an audio device to the VM that plays samples through CRAS server"),
832 Argument::flag("cras-capture", "Enable capturing audio from CRAS server to the cras-audio device"),
833 Argument::flag("null-audio", "Add an audio device to the VM that plays samples to /dev/null"),
834 Argument::value("serial",
835 "type=TYPE,[num=NUM,path=PATH,console]",
836 "Comma seperated key=value pairs for setting up serial devices. Can be given more than once.
837 Possible key values:
838 type=(stdout,syslog,sink,file) - Where to route the serial device
839 num=(1,2,3,4) - Serial Device Number. If not provided, num will default to 1.
840 path=PATH - The path to the file to write to when type=file
841 console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
842 "),
843 Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
844 Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
845 #[cfg(feature = "wl-dmabuf")]
846 Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
847 Argument::short_value('s',
848 "socket",
849 "PATH",
850 "Path to put the control socket. If PATH is a directory, a name will be generated."),
851 Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."),
852 Argument::value("cid", "CID", "Context ID for virtual sockets."),
853 Argument::value("shared-dir", "PATH:TAG",
854 "Directory to be shared with a VM as a source:tag pair. Can be given more than once."),
855 Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."),
856 #[cfg(feature = "plugin")]
857 Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."),
858 #[cfg(feature = "plugin")]
859 Argument::value("plugin-root", "PATH", "Absolute path to a directory that will become root filesystem for the plugin process."),
860 #[cfg(feature = "plugin")]
861 Argument::value("plugin-mount", "PATH:PATH:BOOL", "Path to be mounted into the plugin's root filesystem. Can be given more than once."),
862 #[cfg(feature = "plugin")]
863 Argument::value("plugin-gid-map", "GID:GID:INT", "Supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
864 Argument::flag("vhost-net", "Use vhost for networking."),
865 Argument::value("tap-fd",
866 "fd",
867 "File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
868 #[cfg(feature = "gpu")]
869 Argument::flag("gpu", "(EXPERIMENTAL) enable virtio-gpu device"),
870 #[cfg(feature = "tpm")]
871 Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
872 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"),
873 Argument::value("single-touch", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read single touch input events (such as those from a touchscreen) and write status updates to, optionally followed by width and height (defaults to 800x1280)."),
874 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)."),
875 Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
876 Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
877 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
878 Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
879 Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
880 Argument::short_flag('h', "help", "Print help message.")];
881
882 let mut cfg = Config::default();
883 let match_res = set_arguments(args, &arguments[..], |name, value| {
884 set_argument(&mut cfg, name, value)
885 })
886 .and_then(|_| {
887 if cfg.executable_path.is_none() {
888 return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
889 }
890 if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
891 if cfg.host_ip.is_none() {
892 return Err(argument::Error::ExpectedArgument(
893 "`host_ip` missing from network config".to_owned(),
894 ));
895 }
896 if cfg.netmask.is_none() {
897 return Err(argument::Error::ExpectedArgument(
898 "`netmask` missing from network config".to_owned(),
899 ));
900 }
901 if cfg.mac_address.is_none() {
902 return Err(argument::Error::ExpectedArgument(
903 "`mac` missing from network config".to_owned(),
904 ));
905 }
906 }
907 if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
908 return Err(argument::Error::ExpectedArgument(
909 "`plugin-root` requires `plugin`".to_owned(),
910 ));
911 }
912 Ok(())
913 });
914
915 match match_res {
916 #[cfg(feature = "plugin")]
917 Ok(()) if executable_is_plugin(&cfg.executable_path) => match plugin::run_config(cfg) {
918 Ok(_) => {
919 info!("crosvm and plugin have exited normally");
920 Ok(())
921 }
922 Err(e) => {
923 error!("{}", e);
924 Err(())
925 }
926 },
927 Ok(()) => match linux::run_config(cfg) {
928 Ok(_) => {
929 info!("crosvm has exited normally");
930 Ok(())
931 }
932 Err(e) => {
933 error!("{}", e);
934 Err(())
935 }
936 },
937 Err(argument::Error::PrintHelp) => {
938 print_help("crosvm run", "KERNEL", &arguments[..]);
939 Ok(())
940 }
941 Err(e) => {
942 println!("{}", e);
943 Err(())
944 }
945 }
946 }
947
handle_request( request: &VmRequest, args: std::env::Args, ) -> std::result::Result<VmResponse, ()>948 fn handle_request(
949 request: &VmRequest,
950 args: std::env::Args,
951 ) -> std::result::Result<VmResponse, ()> {
952 let mut return_result = Err(());
953 for socket_path in args {
954 match UnixSeqpacket::connect(&socket_path) {
955 Ok(s) => {
956 let socket: VmControlRequestSocket = MsgSocket::new(s);
957 if let Err(e) = socket.send(request) {
958 error!(
959 "failed to send request to socket at '{}': {}",
960 socket_path, e
961 );
962 return_result = Err(());
963 continue;
964 }
965 match socket.recv() {
966 Ok(response) => return_result = Ok(response),
967 Err(e) => {
968 error!(
969 "failed to send request to socket at2 '{}': {}",
970 socket_path, e
971 );
972 return_result = Err(());
973 continue;
974 }
975 }
976 }
977 Err(e) => {
978 error!("failed to connect to socket at '{}': {}", socket_path, e);
979 return_result = Err(());
980 }
981 }
982 }
983
984 return_result
985 }
986
vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()>987 fn vms_request(request: &VmRequest, args: std::env::Args) -> std::result::Result<(), ()> {
988 let response = handle_request(request, args)?;
989 info!("request response was {}", response);
990 Ok(())
991 }
992
stop_vms(args: std::env::Args) -> std::result::Result<(), ()>993 fn stop_vms(args: std::env::Args) -> std::result::Result<(), ()> {
994 if args.len() == 0 {
995 print_help("crosvm stop", "VM_SOCKET...", &[]);
996 println!("Stops the crosvm instance listening on each `VM_SOCKET` given.");
997 return Err(());
998 }
999 vms_request(&VmRequest::Exit, args)
1000 }
1001
suspend_vms(args: std::env::Args) -> std::result::Result<(), ()>1002 fn suspend_vms(args: std::env::Args) -> std::result::Result<(), ()> {
1003 if args.len() == 0 {
1004 print_help("crosvm suspend", "VM_SOCKET...", &[]);
1005 println!("Suspends the crosvm instance listening on each `VM_SOCKET` given.");
1006 return Err(());
1007 }
1008 vms_request(&VmRequest::Suspend, args)
1009 }
1010
resume_vms(args: std::env::Args) -> std::result::Result<(), ()>1011 fn resume_vms(args: std::env::Args) -> std::result::Result<(), ()> {
1012 if args.len() == 0 {
1013 print_help("crosvm resume", "VM_SOCKET...", &[]);
1014 println!("Resumes the crosvm instance listening on each `VM_SOCKET` given.");
1015 return Err(());
1016 }
1017 vms_request(&VmRequest::Resume, args)
1018 }
1019
balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()>1020 fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
1021 if args.len() < 2 {
1022 print_help("crosvm balloon", "SIZE VM_SOCKET...", &[]);
1023 println!("Set the ballon size of the crosvm instance to `SIZE` bytes.");
1024 return Err(());
1025 }
1026 let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
1027 Ok(n) => n,
1028 Err(_) => {
1029 error!("Failed to parse number of bytes");
1030 return Err(());
1031 }
1032 };
1033
1034 let command = BalloonControlCommand::Adjust { num_bytes };
1035 vms_request(&VmRequest::BalloonCommand(command), args)
1036 }
1037
create_qcow2(mut args: std::env::Args) -> std::result::Result<(), ()>1038 fn create_qcow2(mut args: std::env::Args) -> std::result::Result<(), ()> {
1039 if args.len() != 2 {
1040 print_help("crosvm create_qcow2", "PATH SIZE", &[]);
1041 println!("Create a new QCOW2 image at `PATH` of the specified `SIZE` in bytes.");
1042 return Err(());
1043 }
1044 let file_path = args.nth(0).unwrap();
1045 let size: u64 = match args.nth(0).unwrap().parse::<u64>() {
1046 Ok(n) => n,
1047 Err(_) => {
1048 error!("Failed to parse size of the disk.");
1049 return Err(());
1050 }
1051 };
1052
1053 let file = OpenOptions::new()
1054 .create(true)
1055 .read(true)
1056 .write(true)
1057 .open(&file_path)
1058 .map_err(|e| {
1059 error!("Failed opening qcow file at '{}': {}", file_path, e);
1060 })?;
1061
1062 QcowFile::new(file, size).map_err(|e| {
1063 error!("Failed to create qcow file at '{}': {}", file_path, e);
1064 })?;
1065
1066 Ok(())
1067 }
1068
disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()>1069 fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
1070 if args.len() < 2 {
1071 print_help("crosvm disk", "SUBCOMMAND VM_SOCKET...", &[]);
1072 println!("Manage attached virtual disk devices.");
1073 println!("Subcommands:");
1074 println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET");
1075 return Err(());
1076 }
1077 let subcommand: &str = &args.nth(0).unwrap();
1078
1079 let request = match subcommand {
1080 "resize" => {
1081 let disk_index = match args.nth(0).unwrap().parse::<usize>() {
1082 Ok(n) => n,
1083 Err(_) => {
1084 error!("Failed to parse disk index");
1085 return Err(());
1086 }
1087 };
1088
1089 let new_size = match args.nth(0).unwrap().parse::<u64>() {
1090 Ok(n) => n,
1091 Err(_) => {
1092 error!("Failed to parse disk size");
1093 return Err(());
1094 }
1095 };
1096
1097 VmRequest::DiskCommand {
1098 disk_index,
1099 command: DiskControlCommand::Resize { new_size },
1100 }
1101 }
1102 _ => {
1103 error!("Unknown disk subcommand '{}'", subcommand);
1104 return Err(());
1105 }
1106 };
1107
1108 vms_request(&request, args)
1109 }
1110
1111 enum ModifyUsbError {
1112 ArgMissing(&'static str),
1113 ArgParse(&'static str, String),
1114 ArgParseInt(&'static str, String, ParseIntError),
1115 FailedFdValidate(sys_util::Error),
1116 PathDoesNotExist(PathBuf),
1117 SocketFailed,
1118 UnexpectedResponse(VmResponse),
1119 UnknownCommand(String),
1120 UsbControl(UsbControlResult),
1121 }
1122
1123 impl fmt::Display for ModifyUsbError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1125 use self::ModifyUsbError::*;
1126
1127 match self {
1128 ArgMissing(a) => write!(f, "argument missing: {}", a),
1129 ArgParse(name, value) => {
1130 write!(f, "failed to parse argument {} value `{}`", name, value)
1131 }
1132 ArgParseInt(name, value, e) => write!(
1133 f,
1134 "failed to parse integer argument {} value `{}`: {}",
1135 name, value, e
1136 ),
1137 FailedFdValidate(e) => write!(f, "failed to validate file descriptor: {}", e),
1138 PathDoesNotExist(p) => write!(f, "path `{}` does not exist", p.display()),
1139 SocketFailed => write!(f, "socket failed"),
1140 UnexpectedResponse(r) => write!(f, "unexpected response: {}", r),
1141 UnknownCommand(c) => write!(f, "unknown command: `{}`", c),
1142 UsbControl(e) => write!(f, "{}", e),
1143 }
1144 }
1145 }
1146
1147 type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
1148
parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)>1149 fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
1150 debug!("parse_bus_id_addr: {}", v);
1151 let mut ids = v.split(":");
1152 match (ids.next(), ids.next(), ids.next(), ids.next()) {
1153 (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
1154 let bus_id = bus_id
1155 .parse::<u8>()
1156 .map_err(|e| ModifyUsbError::ArgParseInt("bus_id", bus_id.to_owned(), e))?;
1157 let addr = addr
1158 .parse::<u8>()
1159 .map_err(|e| ModifyUsbError::ArgParseInt("addr", addr.to_owned(), e))?;
1160 let vid = u16::from_str_radix(&vid, 16)
1161 .map_err(|e| ModifyUsbError::ArgParseInt("vid", vid.to_owned(), e))?;
1162 let pid = u16::from_str_radix(&pid, 16)
1163 .map_err(|e| ModifyUsbError::ArgParseInt("pid", pid.to_owned(), e))?;
1164 Ok((bus_id, addr, vid, pid))
1165 }
1166 _ => Err(ModifyUsbError::ArgParse(
1167 "BUS_ID_ADDR_BUS_NUM_DEV_NUM",
1168 v.to_owned(),
1169 )),
1170 }
1171 }
1172
raw_fd_from_path(path: &Path) -> ModifyUsbResult<RawFd>1173 fn raw_fd_from_path(path: &Path) -> ModifyUsbResult<RawFd> {
1174 if !path.exists() {
1175 return Err(ModifyUsbError::PathDoesNotExist(path.to_owned()));
1176 }
1177 let raw_fd = path
1178 .file_name()
1179 .and_then(|fd_osstr| fd_osstr.to_str())
1180 .map_or(
1181 Err(ModifyUsbError::ArgParse(
1182 "USB_DEVICE_PATH",
1183 path.to_string_lossy().into_owned(),
1184 )),
1185 |fd_str| {
1186 fd_str.parse::<libc::c_int>().map_err(|e| {
1187 ModifyUsbError::ArgParseInt("USB_DEVICE_PATH", fd_str.to_owned(), e)
1188 })
1189 },
1190 )?;
1191 validate_raw_fd(raw_fd).map_err(ModifyUsbError::FailedFdValidate)
1192 }
1193
usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult>1194 fn usb_attach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
1195 let val = args
1196 .next()
1197 .ok_or(ModifyUsbError::ArgMissing("BUS_ID_ADDR_BUS_NUM_DEV_NUM"))?;
1198 let (bus, addr, vid, pid) = parse_bus_id_addr(&val)?;
1199 let dev_path = PathBuf::from(
1200 args.next()
1201 .ok_or(ModifyUsbError::ArgMissing("usb device path"))?,
1202 );
1203 let usb_file: Option<File> = if dev_path == Path::new("-") {
1204 None
1205 } else if dev_path.parent() == Some(Path::new("/proc/self/fd")) {
1206 // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
1207 // Safe because we will validate |raw_fd|.
1208 Some(unsafe { File::from_raw_fd(raw_fd_from_path(&dev_path)?) })
1209 } else {
1210 Some(
1211 OpenOptions::new()
1212 .read(true)
1213 .write(true)
1214 .open(&dev_path)
1215 .map_err(|_| ModifyUsbError::UsbControl(UsbControlResult::FailedToOpenDevice))?,
1216 )
1217 };
1218
1219 let request = VmRequest::UsbCommand(UsbControlCommand::AttachDevice {
1220 bus,
1221 addr,
1222 vid,
1223 pid,
1224 fd: usb_file.map(MaybeOwnedFd::Owned),
1225 });
1226 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
1227 match response {
1228 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
1229 r => Err(ModifyUsbError::UnexpectedResponse(r)),
1230 }
1231 }
1232
usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult>1233 fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
1234 let port: u8 = args
1235 .next()
1236 .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
1237 p.parse::<u8>()
1238 .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
1239 })?;
1240 let request = VmRequest::UsbCommand(UsbControlCommand::DetachDevice { port });
1241 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
1242 match response {
1243 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
1244 r => Err(ModifyUsbError::UnexpectedResponse(r)),
1245 }
1246 }
1247
usb_list(args: std::env::Args) -> ModifyUsbResult<UsbControlResult>1248 fn usb_list(args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
1249 let mut ports: [u8; USB_CONTROL_MAX_PORTS] = Default::default();
1250 for (index, port) in ports.iter_mut().enumerate() {
1251 *port = index as u8
1252 }
1253 let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { ports });
1254 let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
1255 match response {
1256 VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
1257 r => Err(ModifyUsbError::UnexpectedResponse(r)),
1258 }
1259 }
1260
modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()>1261 fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
1262 if args.len() < 2 {
1263 print_help("crosvm usb",
1264 "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
1265 return Err(());
1266 }
1267
1268 // This unwrap will not panic because of the above length check.
1269 let command = args.next().unwrap();
1270 let result = match command.as_ref() {
1271 "attach" => usb_attach(args),
1272 "detach" => usb_detach(args),
1273 "list" => usb_list(args),
1274 other => Err(ModifyUsbError::UnknownCommand(other.to_owned())),
1275 };
1276 match result {
1277 Ok(response) => {
1278 println!("{}", response);
1279 Ok(())
1280 }
1281 Err(e) => {
1282 println!("error {}", e);
1283 Err(())
1284 }
1285 }
1286 }
1287
print_usage()1288 fn print_usage() {
1289 print_help("crosvm", "[stop|run]", &[]);
1290 println!("Commands:");
1291 println!(" stop - Stops crosvm instances via their control sockets.");
1292 println!(" run - Start a new crosvm instance.");
1293 println!(" create_qcow2 - Create a new qcow2 disk image file.");
1294 println!(" disk - Manage attached virtual disk devices.");
1295 println!(" usb - Manage attached virtual USB devices.");
1296 }
1297
crosvm_main() -> std::result::Result<(), ()>1298 fn crosvm_main() -> std::result::Result<(), ()> {
1299 if let Err(e) = syslog::init() {
1300 println!("failed to initialize syslog: {}", e);
1301 return Err(());
1302 }
1303
1304 panic_hook::set_panic_hook();
1305
1306 let mut args = std::env::args();
1307 if args.next().is_none() {
1308 error!("expected executable name");
1309 return Err(());
1310 }
1311
1312 // Past this point, usage of exit is in danger of leaking zombie processes.
1313 let ret = match args.next().as_ref().map(|a| a.as_ref()) {
1314 None => {
1315 print_usage();
1316 Ok(())
1317 }
1318 Some("stop") => stop_vms(args),
1319 Some("suspend") => suspend_vms(args),
1320 Some("resume") => resume_vms(args),
1321 Some("run") => run_vm(args),
1322 Some("balloon") => balloon_vms(args),
1323 Some("create_qcow2") => create_qcow2(args),
1324 Some("disk") => disk_cmd(args),
1325 Some("usb") => modify_usb(args),
1326 Some(c) => {
1327 println!("invalid subcommand: {:?}", c);
1328 print_usage();
1329 Err(())
1330 }
1331 };
1332
1333 // Reap exit status from any child device processes. At this point, all devices should have been
1334 // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
1335 // take some time for the processes to shut down.
1336 if !wait_all_children() {
1337 // We gave them a chance, and it's too late.
1338 warn!("not all child processes have exited; sending SIGKILL");
1339 if let Err(e) = kill_process_group() {
1340 // We're now at the mercy of the OS to clean up after us.
1341 warn!("unable to kill all child processes: {}", e);
1342 }
1343 }
1344
1345 // WARNING: Any code added after this point is not guaranteed to run
1346 // since we may forcibly kill this process (and its children) above.
1347 ret
1348 }
1349
main()1350 fn main() {
1351 std::process::exit(if crosvm_main().is_ok() { 0 } else { 1 });
1352 }
1353
1354 #[cfg(test)]
1355 mod tests {
1356 use super::*;
1357
1358 #[test]
parse_cpu_set_single()1359 fn parse_cpu_set_single() {
1360 assert_eq!(parse_cpu_set("123").expect("parse failed"), vec![123]);
1361 }
1362
1363 #[test]
parse_cpu_set_list()1364 fn parse_cpu_set_list() {
1365 assert_eq!(
1366 parse_cpu_set("0,1,2,3").expect("parse failed"),
1367 vec![0, 1, 2, 3]
1368 );
1369 }
1370
1371 #[test]
parse_cpu_set_range()1372 fn parse_cpu_set_range() {
1373 assert_eq!(
1374 parse_cpu_set("0-3").expect("parse failed"),
1375 vec![0, 1, 2, 3]
1376 );
1377 }
1378
1379 #[test]
parse_cpu_set_list_of_ranges()1380 fn parse_cpu_set_list_of_ranges() {
1381 assert_eq!(
1382 parse_cpu_set("3-4,7-9,18").expect("parse failed"),
1383 vec![3, 4, 7, 8, 9, 18]
1384 );
1385 }
1386
1387 #[test]
parse_cpu_set_repeated()1388 fn parse_cpu_set_repeated() {
1389 // For now, allow duplicates - they will be handled gracefully by the vec to cpu_set_t conversion.
1390 assert_eq!(parse_cpu_set("1,1,1").expect("parse failed"), vec![1, 1, 1]);
1391 }
1392
1393 #[test]
parse_cpu_set_negative()1394 fn parse_cpu_set_negative() {
1395 // Negative CPU numbers are not allowed.
1396 parse_cpu_set("-3").expect_err("parse should have failed");
1397 }
1398
1399 #[test]
parse_cpu_set_reverse_range()1400 fn parse_cpu_set_reverse_range() {
1401 // Ranges must be from low to high.
1402 parse_cpu_set("5-2").expect_err("parse should have failed");
1403 }
1404
1405 #[test]
parse_cpu_set_open_range()1406 fn parse_cpu_set_open_range() {
1407 parse_cpu_set("3-").expect_err("parse should have failed");
1408 }
1409
1410 #[test]
parse_cpu_set_extra_comma()1411 fn parse_cpu_set_extra_comma() {
1412 parse_cpu_set("0,1,2,").expect_err("parse should have failed");
1413 }
1414
1415 #[test]
parse_serial_vaild()1416 fn parse_serial_vaild() {
1417 parse_serial_options("type=syslog,num=1,console=true").expect("parse should have succeded");
1418 }
1419
1420 #[test]
parse_serial_valid_no_num()1421 fn parse_serial_valid_no_num() {
1422 parse_serial_options("type=syslog").expect("parse should have succeded");
1423 }
1424
1425 #[test]
parse_serial_invalid_type()1426 fn parse_serial_invalid_type() {
1427 parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
1428 }
1429
1430 #[test]
parse_serial_invalid_num_upper()1431 fn parse_serial_invalid_num_upper() {
1432 parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
1433 }
1434
1435 #[test]
parse_serial_invalid_num_lower()1436 fn parse_serial_invalid_num_lower() {
1437 parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
1438 }
1439
1440 #[test]
parse_serial_invalid_num_string()1441 fn parse_serial_invalid_num_string() {
1442 parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
1443 }
1444
1445 #[test]
parse_serial_invalid_option()1446 fn parse_serial_invalid_option() {
1447 parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
1448 }
1449 }
1450