• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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