diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 145 |
1 files changed, 110 insertions, 35 deletions
diff --git a/src/main.rs b/src/main.rs index e33be66..7fd3eca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use std::string::String; use std::thread::sleep; use std::time::Duration; -use arch::Pstore; +use arch::{set_default_serial_parameters, Pstore, SerialHardware, SerialParameters, SerialType}; use audio_streams::StreamEffect; use crosvm::{ argument::{self, print_help, set_arguments, Argument}, @@ -25,7 +25,7 @@ use crosvm::{ }; #[cfg(feature = "gpu")] use devices::virtio::gpu::{GpuMode, GpuParameters}; -use devices::{Ac97Backend, Ac97Parameters, SerialParameters, SerialType}; +use devices::{Ac97Backend, Ac97Parameters}; use disk::QcowFile; use msg_socket::{MsgReceiver, MsgSender, MsgSocket}; use sys_util::{ @@ -74,7 +74,7 @@ fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> { let mut cpuset = Vec::new(); for part in s.split(',') { let range: Vec<&str> = part.split('-').collect(); - if range.len() == 0 || range.len() > 2 { + if range.is_empty() || range.len() > 2 { return Err(argument::Error::InvalidValue { value: part.to_owned(), expected: String::from("invalid list syntax"), @@ -117,8 +117,8 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> { if let Some(s) = s { let opts = s - .split(",") - .map(|frag| frag.split("=")) + .split(',') + .map(|frag| frag.split('=')) .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); for (k, v) in opts { @@ -252,8 +252,8 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> { let mut ac97_params: Ac97Parameters = Default::default(); let opts = s - .split(",") - .map(|frag| frag.split("=")) + .split(',') + .map(|frag| frag.split('=')) .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); for (k, v) in opts { @@ -273,7 +273,7 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> { } "capture_effects" => { ac97_params.capture_effects = v - .split("|") + .split('|') .map(|val| { val.parse::<StreamEffect>() .map_err(|e| argument::Error::InvalidValue { @@ -298,19 +298,27 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> { fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> { let mut serial_setting = SerialParameters { type_: SerialType::Sink, + hardware: SerialHardware::Serial, path: None, + input: None, num: 1, console: false, + earlycon: false, stdin: false, }; let opts = s - .split(",") - .map(|frag| frag.split("=")) + .split(',') + .map(|frag| frag.split('=')) .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or(""))); for (k, v) in opts { match k { + "hardware" => { + serial_setting.hardware = v + .parse::<SerialHardware>() + .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))? + } "type" => { serial_setting.type_ = v .parse::<SerialType>() @@ -336,12 +344,33 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> { )) })? } + "earlycon" => { + serial_setting.earlycon = v.parse::<bool>().map_err(|e| { + argument::Error::Syntax(format!( + "serial device earlycon is not parseable: {}", + e, + )) + })? + } "stdin" => { serial_setting.stdin = v.parse::<bool>().map_err(|e| { argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e)) - })? + })?; + if serial_setting.stdin && serial_setting.input.is_some() { + return Err(argument::Error::TooManyArguments( + "Cannot specify both stdin and input options".to_string(), + )); + } } "path" => serial_setting.path = Some(PathBuf::from(v)), + "input" => { + if serial_setting.stdin { + return Err(argument::Error::TooManyArguments( + "Cannot specify both stdin and input options".to_string(), + )); + } + serial_setting.input = Some(PathBuf::from(v)); + } _ => { return Err(argument::Error::UnknownArgument(format!( "serial parameter {}", @@ -355,7 +384,7 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> { } fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> { - let components: Vec<&str> = value.split(":").collect(); + let components: Vec<&str> = value.split(':').collect(); if components.is_empty() || components.len() > 3 || components[0].is_empty() { return Err(argument::Error::InvalidValue { value: value.to_owned(), @@ -402,7 +431,7 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> { } fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> { - let components: Vec<&str> = value.split(":").collect(); + let components: Vec<&str> = value.split(':').collect(); if components.is_empty() || components.len() > 3 || components[0].is_empty() { return Err(argument::Error::InvalidValue { value: value.to_owned(), @@ -501,7 +530,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: ) } "cpu-affinity" => { - if cfg.vcpu_affinity.len() != 0 { + if !cfg.vcpu_affinity.is_empty() { return Err(argument::Error::TooManyArguments( "`cpu-affinity` already given".to_owned(), )); @@ -532,10 +561,11 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: "serial" => { let serial_params = parse_serial_options(value.unwrap())?; let num = serial_params.num; - if cfg.serial_parameters.contains_key(&num) { + let key = (serial_params.hardware, num); + if cfg.serial_parameters.contains_key(&key) { return Err(argument::Error::TooManyArguments(format!( - "serial num {}", - num + "serial hardware {} num {}", + serial_params.hardware, num, ))); } @@ -543,8 +573,29 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: for params in cfg.serial_parameters.values() { if params.console { return Err(argument::Error::TooManyArguments(format!( - "serial device {} already set as console", - params.num + "{} device {} already set as console", + params.hardware, params.num, + ))); + } + } + } + + if serial_params.earlycon { + // Only SerialHardware::Serial supports earlycon= currently. + match serial_params.hardware { + SerialHardware::Serial => {} + _ => { + return Err(argument::Error::InvalidValue { + value: serial_params.hardware.to_string().to_owned(), + expected: String::from("earlycon not supported for hardware"), + }); + } + } + for params in cfg.serial_parameters.values() { + if params.earlycon { + return Err(argument::Error::TooManyArguments(format!( + "{} device {} already set as earlycon", + params.hardware, params.num, ))); } } @@ -553,13 +604,13 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: if serial_params.stdin { if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) { return Err(argument::Error::TooManyArguments(format!( - "serial device {} already connected to standard input", - previous_stdin.num + "{} device {} already connected to standard input", + previous_stdin.hardware, previous_stdin.num, ))); } } - cfg.serial_parameters.insert(num, serial_params); + cfg.serial_parameters.insert(key, serial_params); } "syslog-tag" => { if cfg.syslog_tag.is_some() { @@ -1030,7 +1081,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: let reader = BufReader::new(file); for l in reader.lines() { let line = l.unwrap(); - let trimmed_line = line.splitn(2, '#').nth(0).unwrap().trim(); + let trimmed_line = line.splitn(2, '#').next().unwrap().trim(); if !trimmed_line.is_empty() { let mount = parse_plugin_mount_option(trimmed_line)?; cfg.plugin_mounts.push(mount); @@ -1049,7 +1100,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: let reader = BufReader::new(file); for l in reader.lines() { let line = l.unwrap(); - let trimmed_line = line.splitn(2, '#').nth(0).unwrap().trim(); + let trimmed_line = line.splitn(2, '#').next().unwrap().trim(); if !trimmed_line.is_empty() { let map = parse_plugin_gid_map_option(trimmed_line)?; cfg.plugin_gid_maps.push(map); @@ -1084,7 +1135,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: "`single-touch` already given".to_owned(), )); } - let mut it = value.unwrap().split(":"); + let mut it = value.unwrap().split(':'); let mut single_touch_spec = TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned())); @@ -1102,7 +1153,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: "`trackpad` already given".to_owned(), )); } - let mut it = value.unwrap().split(":"); + let mut it = value.unwrap().split(':'); let mut trackpad_spec = TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned())); @@ -1214,6 +1265,7 @@ fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Err } } } + set_default_serial_parameters(&mut cfg.serial_parameters); Ok(()) } @@ -1326,10 +1378,9 @@ Possible key values: effect value now is EchoCancellation or aec. "#, ), - Argument::value( "serial", - "type=TYPE,[num=NUM,path=PATH,console,stdin]", + "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]", "\ Comma separated key=value pairs for setting up serial devices. Can be given more than once. @@ -1339,16 +1390,26 @@ Possible key values: type=(stdout,syslog,sink,file) Where to route the serial device. + hardware=(serial,virtio-console) + Which type of serial hardware to emulate. Defaults to 8250 UART + (serial). + num=(1,2,3,4) Serial Device Number. If not provided, num will default to 1. path=PATH The path to the file to write to when type=file. + input=PATH + The path to the file to read from when not stdin. + console Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided. + earlycon + Use this serial device as the early console. Can only be given once. + stdin Direct standard input to this serial device. Can only be given once. Will default to first serial port if not provided. @@ -1738,7 +1799,7 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> { println!("Set the balloon size of the crosvm instance to `SIZE` bytes."); return Err(()); } - let num_bytes = match args.nth(0).unwrap().parse::<u64>() { + let num_bytes = match args.next().unwrap().parse::<u64>() { Ok(n) => n, Err(_) => { error!("Failed to parse number of bytes"); @@ -1750,6 +1811,19 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> { vms_request(&VmRequest::BalloonCommand(command), args) } +fn balloon_stats(args: std::env::Args) -> std::result::Result<(), ()> { + if args.len() != 1 { + print_help("crosvm balloon_stats", "VM_SOCKET", &[]); + println!("Prints virtio balloon statistics for a `VM_SOCKET`."); + return Err(()); + } + let command = BalloonControlCommand::Stats {}; + let request = &VmRequest::BalloonCommand(command); + let response = handle_request(request, args)?; + println!("{}", response); + Ok(()) +} + fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> { let arguments = [ Argument::positional("PATH", "where to create the qcow2 image"), @@ -1796,7 +1870,7 @@ fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> { .map_err(|e| { error!("Unable to parse command line arguments: {}", e); })?; - if file_path.len() == 0 || !(size.is_some() ^ backing_file.is_some()) { + if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) { print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments); println!( "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or @@ -1837,11 +1911,11 @@ fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> { println!(" resize DISK_INDEX NEW_SIZE VM_SOCKET"); return Err(()); } - let subcommand: &str = &args.nth(0).unwrap(); + let subcommand: &str = &args.next().unwrap(); let request = match subcommand { "resize" => { - let disk_index = match args.nth(0).unwrap().parse::<usize>() { + let disk_index = match args.next().unwrap().parse::<usize>() { Ok(n) => n, Err(_) => { error!("Failed to parse disk index"); @@ -1849,7 +1923,7 @@ fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> { } }; - let new_size = match args.nth(0).unwrap().parse::<u64>() { + let new_size = match args.next().unwrap().parse::<u64>() { Ok(n) => n, Err(_) => { error!("Failed to parse disk size"); @@ -1911,7 +1985,7 @@ type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>; fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> { debug!("parse_bus_id_addr: {}", v); - let mut ids = v.split(":"); + let mut ids = v.split(':'); match (ids.next(), ids.next(), ids.next(), ids.next()) { (Some(bus_id), Some(addr), Some(vid), Some(pid)) => { let bus_id = bus_id @@ -2069,7 +2143,7 @@ fn pkg_version() -> std::result::Result<(), ()> { print!("crosvm {}", VERSION.unwrap_or("UNKNOWN")); match PKG_VERSION { Some(v) => println!("-{}", v), - None => println!(""), + None => println!(), } Ok(()) } @@ -2099,6 +2173,7 @@ fn crosvm_main() -> std::result::Result<(), ()> { Some("resume") => resume_vms(args), Some("run") => run_vm(args), Some("balloon") => balloon_vms(args), + Some("balloon_stats") => balloon_stats(args), Some("create_qcow2") => create_qcow2(args), Some("disk") => disk_cmd(args), Some("usb") => modify_usb(args), |