crosvm: carve-out unix specific set and get arguments

BUG=b:227659915
TEST=presubmit

Change-Id: I1e7080c6cb19ffe34c052524933711b12d750b2f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3643041
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Vikram Auradkar <auradkar@google.com>
Reviewed-by: Noah Gold <nkgold@google.com>
This commit is contained in:
Vikram Auradkar 2022-05-11 13:59:27 -07:00 committed by Chromeos LUCI
parent a6945f4a49
commit 5b3cfb9143
4 changed files with 433 additions and 385 deletions

View file

@ -5,6 +5,7 @@
//! Runs a virtual machine
pub mod panic_hook;
pub mod sys;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryFrom;
@ -24,11 +25,9 @@ use arch::{
set_default_serial_parameters, MsrAction, MsrConfig, MsrValueFrom, Pstore, VcpuAffinity,
};
use base::{debug, error, getpid, info, kill_process_group, pagesize, reap_child, syslog, warn};
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
use crosvm::platform::GpuRenderServerParameters;
use crosvm::{
argument::{self, parse_hex_or_decimal, print_help, set_arguments, Argument},
platform, BindMount, Config, Executable, FileBackedMappingParameters, GidMap, SharedDir,
platform, BindMount, Config, Executable, FileBackedMappingParameters, GidMap,
TouchDeviceOption, VfioCommand, VhostUserFsOption, VhostUserOption, VhostUserWlOption,
VvuOption,
};
@ -574,56 +573,6 @@ fn parse_gpu_display_options(
Ok(())
}
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
fn parse_gpu_render_server_options(s: Option<&str>) -> argument::Result<GpuRenderServerParameters> {
let mut path: Option<PathBuf> = None;
let mut cache_path = None;
let mut cache_size = None;
if let Some(s) = s {
let opts = s
.split(',')
.map(|frag| frag.split('='))
.map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
for (k, v) in opts {
match k {
"path" => {
path =
Some(
PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
value: v.to_string(),
expected: e.to_string(),
})?,
)
}
"cache-path" => cache_path = Some(v.to_string()),
"cache-size" => cache_size = Some(v.to_string()),
"" => {}
_ => {
return Err(argument::Error::UnknownArgument(format!(
"gpu-render-server parameter {}",
k
)));
}
}
}
}
if let Some(p) = path {
Ok(GpuRenderServerParameters {
path: p,
cache_path,
cache_size,
})
} else {
Err(argument::Error::InvalidValue {
value: s.unwrap_or("").to_string(),
expected: String::from("gpu-render-server must include 'path'"),
})
}
}
#[cfg(feature = "audio")]
fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
let mut ac97_params: Ac97Parameters = Default::default();
@ -1101,39 +1050,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
cfg.kvm_device_path = kvm_device_path;
}
"vhost-vsock-fd" => {
if cfg.vhost_vsock_device.is_some() {
return Err(argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("A vhost-vsock device was already specified"),
});
}
let fd: i32 = value
.unwrap()
.parse()
.map_err(|_| argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("this value for `vhost-vsock-fd` needs to be integer"),
})?;
cfg.vhost_vsock_device = Some(PathBuf::from(format!("/proc/self/fd/{}", fd)));
}
"vhost-vsock-device" => {
if cfg.vhost_vsock_device.is_some() {
return Err(argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("A vhost-vsock device was already specified"),
});
}
let vhost_vsock_device_path = PathBuf::from(value.unwrap());
if !vhost_vsock_device_path.exists() {
return Err(argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("this vhost-vsock device path does not exist"),
});
}
cfg.vhost_vsock_device = Some(vhost_vsock_device_path);
}
"vhost-net-device" => {
let vhost_net_device_path = PathBuf::from(value.unwrap());
if !vhost_net_device_path.exists() {
@ -1211,15 +1127,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
cfg.vcpu_cgroup_path = Some(vcpu_cgroup_path);
}
#[cfg(feature = "audio_cras")]
"cras-snd" => {
cfg.cras_snds.push(
value
.unwrap()
.parse()
.map_err(|e: CrasSndError| argument::Error::Syntax(e.to_string()))?,
);
}
"no-smt" => {
cfg.no_smt = true;
}
@ -1447,23 +1354,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
},
});
}
"host_ip" => {
if cfg.host_ip.is_some() {
return Err(argument::Error::TooManyArguments(
"`host_ip` already given".to_owned(),
));
}
cfg.host_ip =
Some(
value
.unwrap()
.parse()
.map_err(|_| argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
})?,
)
}
"netmask" => {
if cfg.netmask.is_some() {
return Err(argument::Error::TooManyArguments(
@ -1629,150 +1519,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
})?,
);
}
"shared-dir" => {
// This is formatted as multiple fields, each separated by ":". The first 2 fields are
// fixed (src:tag). The rest may appear in any order:
//
// * type=TYPE - must be one of "p9" or "fs" (default: p9)
// * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
// (default: "0 <current euid> 1")
// * gidmap=GIDMAP - a gid map in the same format as uidmap
// (default: "0 <current egid> 1")
// * privileged_quota_uids=UIDS - Space-separated list of privileged uid values. When
// performing quota-related operations, these UIDs are treated as if they have
// CAP_FOWNER.
// * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
// and directory contents should be considered valid (default: 5)
// * cache=CACHE - one of "never", "always", or "auto" (default: auto)
// * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
let param = value.unwrap();
let mut components = param.split(':');
let src =
PathBuf::from(
components
.next()
.ok_or_else(|| argument::Error::InvalidValue {
value: param.to_owned(),
expected: String::from("missing source path for `shared-dir`"),
})?,
);
let tag = components
.next()
.ok_or_else(|| argument::Error::InvalidValue {
value: param.to_owned(),
expected: String::from("missing tag for `shared-dir`"),
})?
.to_owned();
if !src.is_dir() {
return Err(argument::Error::InvalidValue {
value: param.to_owned(),
expected: String::from("source path for `shared-dir` must be a directory"),
});
}
let mut shared_dir = SharedDir {
src,
tag,
..Default::default()
};
for opt in components {
let mut o = opt.splitn(2, '=');
let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
value: opt.to_owned(),
expected: String::from("`shared-dir` options must not be empty"),
})?;
let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
value: opt.to_owned(),
expected: String::from("`shared-dir` options must be of the form `kind=value`"),
})?;
match kind {
"type" => {
shared_dir.kind =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`type` must be one of `fs` or `9p`"),
})?
}
"uidmap" => shared_dir.uid_map = value.into(),
"gidmap" => shared_dir.gid_map = value.into(),
#[cfg(feature = "chromeos")]
"privileged_quota_uids" => {
shared_dir.fs_cfg.privileged_quota_uids =
value.split(' ').map(|s| s.parse().unwrap()).collect();
}
"timeout" => {
let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`timeout` must be an integer"),
})?;
let dur = Duration::from_secs(seconds);
shared_dir.fs_cfg.entry_timeout = dur;
shared_dir.fs_cfg.attr_timeout = dur;
}
"cache" => {
let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from(
"`cache` must be one of `never`, `always`, or `auto`",
),
})?;
shared_dir.fs_cfg.cache_policy = policy;
}
"writeback" => {
let writeback =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`writeback` must be a boolean"),
})?;
shared_dir.fs_cfg.writeback = writeback;
}
"rewrite-security-xattrs" => {
let rewrite_security_xattrs =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from(
"`rewrite-security-xattrs` must be a boolean",
),
})?;
shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
}
"ascii_casefold" => {
let ascii_casefold =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`ascii_casefold` must be a boolean"),
})?;
shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
}
"dax" => {
let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`dax` must be a boolean"),
})?;
shared_dir.fs_cfg.use_dax = use_dax;
}
"posix_acl" => {
let posix_acl =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`posix_acl` must be a boolean"),
})?;
shared_dir.fs_cfg.posix_acl = posix_acl;
}
_ => {
return Err(argument::Error::InvalidValue {
value: kind.to_owned(),
expected: String::from("unrecognized option for `shared-dir`"),
})
}
}
}
cfg.shared_dirs.push(shared_dir);
}
"seccomp-policy-dir" => {
if let Some(jail_config) = &mut cfg.jail_config {
// `value` is Some because we are in this match so it's safe to unwrap.
@ -1859,22 +1605,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
}
}
"vhost-net" => cfg.vhost_net = true,
"tap-fd" => {
cfg.tap_fd.push(
value
.unwrap()
.parse()
.map_err(|_| argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from(
"this value for `tap-fd` must be an unsigned integer",
),
})?,
);
}
"tap-name" => {
cfg.tap_name.push(value.unwrap().to_owned());
}
#[cfg(feature = "gpu")]
"gpu" => {
let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
@ -1885,10 +1615,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
let gpu_parameters = cfg.gpu_parameters.get_or_insert_with(Default::default);
parse_gpu_display_options(value, gpu_parameters)?;
}
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
"gpu-render-server" => {
cfg.gpu_render_server_parameters = Some(parse_gpu_render_server_options(value)?);
}
"software-tpm" => {
cfg.software_tpm = true;
}
@ -2308,63 +2034,6 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
cfg.vvu_proxy.push(vvu_opt);
}
"coiommu" => {
let mut params: devices::CoIommuParameters = Default::default();
if let Some(v) = value {
let opts = v
.split(',')
.map(|frag| frag.splitn(2, '='))
.map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
for (k, v) in opts {
match k {
"unpin_policy" => {
params.unpin_policy = v
.parse::<devices::CoIommuUnpinPolicy>()
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
}
"unpin_interval" => {
params.unpin_interval =
Duration::from_secs(v.parse::<u64>().map_err(|e| {
argument::Error::UnknownArgument(format!("{}", e))
})?)
}
"unpin_limit" => {
let limit = v
.parse::<u64>()
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?;
if limit == 0 {
return Err(argument::Error::InvalidValue {
value: v.to_owned(),
expected: String::from("Please use non-zero unpin_limit value"),
});
}
params.unpin_limit = Some(limit)
}
"unpin_gen_threshold" => {
params.unpin_gen_threshold = v
.parse::<u64>()
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
}
_ => {
return Err(argument::Error::UnknownArgument(format!(
"coiommu parameter {}",
k
)));
}
}
}
}
if cfg.coiommu_param.is_some() {
return Err(argument::Error::TooManyArguments(
"coiommu param already given".to_owned(),
));
}
cfg.coiommu_param = Some(params);
}
"file-backed-mapping" => {
cfg.file_backed_mappings
.push(parse_file_backed_mapping(value)?);
@ -2534,7 +2203,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
cfg.pcie_ecam = Some(MemRegion { base, size: len });
}
"help" => return Err(argument::Error::PrintHelp),
_ => unreachable!(),
_ => sys::set_arguments(cfg, name, value)?,
}
Ok(())
}
@ -2715,10 +2384,9 @@ enum CommandStatus {
}
fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
let arguments =
&[Argument::positional("KERNEL", "bzImage of kernel to run"),
let mut arguments =
vec![Argument::positional("KERNEL", "bzImage of kernel to run"),
Argument::value("kvm-device", "PATH", "Path to the KVM device. (default /dev/kvm)"),
Argument::value("vhost-vsock-fd", "FD", "Open FD to the vhost-vsock device, mutually exclusive with vhost-vsock-device."),
Argument::value("vhost-vsock-device", "PATH", "Path to the vhost-vsock device. (default /dev/vhost-vsock)"),
Argument::value("vhost-net-device", "PATH", "Path to the vhost-net device. (default /dev/vhost-net)"),
Argument::value("android-fstab", "PATH", "Path to Android fstab"),
@ -2736,20 +2404,6 @@ fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
making all vCPU threads share same cookie for core scheduling.
This option is no-op on devices that have neither MDS nor L1TF vulnerability."),
Argument::value("vcpu-cgroup-path", "PATH", "Move all vCPU threads to this CGroup (default: nothing moves)."),
#[cfg(feature = "audio_cras")]
Argument::value("cras-snd",
"[capture=true,client=crosvm,socket=unified,num_output_streams=1,num_input_streams=1]",
"Comma separated key=value pairs for setting up cras snd devices.
Possible key values:
capture - Enable audio capture. Default to false.
client_type - Set specific client type for cras backend.
num_output_streams - Set number of output PCM streams
num_input_streams - Set number of input PCM streams"),
Argument::flag("no-smt", "Don't use SMT in the guest"),
Argument::value("rt-cpus", "CPUSET", "Comma-separated list of CPUs or CPU ranges to run VCPUs on. (e.g. 0,1-3,5) (default: none)"),
Argument::flag("delay-rt", "Don't set VCPUs real-time until make-rt command is run"),
@ -2785,9 +2439,6 @@ fn run_vm(args: std::env::Args) -> std::result::Result<CommandStatus, ()> {
Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
Argument::value("pmem-device", "PATH", "Path to a disk image."),
Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file followed by size."),
Argument::value("host_ip",
"IP",
"IP address to assign to host tap interface."),
Argument::value("netmask", "NETMASK", "Netmask for VM subnet."),
Argument::value("mac", "MAC", "MAC address for VM."),
Argument::value("net-vq-pairs", "N", "virtio net virtual queue paris. (default: 1)"),
@ -2887,12 +2538,6 @@ There is a cost of slightly increased latency the first time the file is accesse
#[cfg(feature = "plugin")]
Argument::value("plugin-gid-map-file", "PATH", "Path to the file listing supplemental GIDs that should be mapped in plugin jail. Can be given more than once."),
Argument::flag("vhost-net", "Use vhost for networking."),
Argument::value("tap-name",
"NAME",
"Name of a configured persistent TAP interface to use for networking. A different virtual network card will be added each time this argument is given."),
Argument::value("tap-fd",
"fd",
"File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
#[cfg(feature = "gpu")]
Argument::flag_or_value("gpu",
"[width=INT,height=INT]",
@ -2931,18 +2576,6 @@ There is a cost of slightly increased latency the first time the file is accesse
width=INT - The width of the virtual display connected to the virtio-gpu.
height=INT - The height of the virtual display connected to the virtio-gpu."),
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
Argument::flag_or_value("gpu-render-server",
"[path=PATH]",
"(EXPERIMENTAL) Comma separated key=value pairs for setting up a render server for the virtio-gpu device
Possible key values:
path=PATH - The path to the render server executable.
cache-path=PATH - The path to the render server shader cache.
cache-size=SIZE - The maximum size of the shader cache."),
#[cfg(feature = "tpm")]
Argument::flag("software-tpm", "enable a software emulated trusted platform module device"),
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"),
@ -3045,19 +2678,6 @@ iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
subsystem_device=NUM - PCI subsystem device ID
revision=NUM - revision"),
Argument::flag_or_value("coiommu",
"unpin_policy=POLICY,unpin_interval=NUM,unpin_limit=NUM,unpin_gen_threshold=NUM ",
"Comma separated key=value pairs for setting up coiommu devices.
Possible key values:
unpin_policy=lru - LRU unpin policy.
unpin_interval=NUM - Unpin interval time in seconds.
unpin_limit=NUM - Unpin limit for each unpin cycle, in unit of page count. 0 is invalid.
unpin_gen_threshold=NUM - Number of unpin intervals a pinned page must be busy for to be aged into the older which is less frequently checked generation."),
Argument::value("file-backed-mapping", "addr=NUM,size=SIZE,path=PATH[,offset=NUM][,ro][,rw][,sync]", "Map the given file into guest memory at the specified address.
Parameters (addr, size, path are required):
@ -3094,6 +2714,7 @@ iommu=on|off - indicates whether to enable virtio IOMMU for this device"),
"Base and length for PCIE Enhanced Configuration Access Mechanism"),
Argument::short_flag('h', "help", "Print help message.")];
arguments.append(&mut sys::get_arguments());
let mut cfg = Config::default();
let match_res = set_arguments(args, &arguments[..], |name, value| {
set_argument(&mut cfg, name, value)

14
src/sys.rs Normal file
View file

@ -0,0 +1,14 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
cfg_if::cfg_if! {
if #[cfg(unix)] {
pub(crate) mod unix;
use unix as platform;
} else {
compile_error!("Unsupported platform");
}
}
pub(crate) use platform::main::{get_arguments, set_arguments};

5
src/sys/unix.rs Normal file
View file

@ -0,0 +1,5 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
pub(crate) mod main;

408
src/sys/unix/main.rs Normal file
View file

@ -0,0 +1,408 @@
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
use std::str::FromStr;
use std::{path::PathBuf, time::Duration};
#[cfg(feature = "audio_cras")]
use devices::virtio::{
snd::cras_backend::Error as CrasSndError, vhost::user::device::run_cras_snd_device,
};
use crate::argument::{self, Argument};
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
use crate::platform::GpuRenderServerParameters;
use crosvm::{Config, SharedDir};
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
fn parse_gpu_render_server_options(s: Option<&str>) -> argument::Result<GpuRenderServerParameters> {
let mut path: Option<PathBuf> = None;
let mut cache_path = None;
let mut cache_size = None;
if let Some(s) = s {
let opts = s
.split(',')
.map(|frag| frag.split('='))
.map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
for (k, v) in opts {
match k {
"path" => {
path =
Some(
PathBuf::from_str(v).map_err(|e| argument::Error::InvalidValue {
value: v.to_string(),
expected: e.to_string(),
})?,
)
}
"cache-path" => cache_path = Some(v.to_string()),
"cache-size" => cache_size = Some(v.to_string()),
"" => {}
_ => {
return Err(argument::Error::UnknownArgument(format!(
"gpu-render-server parameter {}",
k
)));
}
}
}
}
if let Some(p) = path {
Ok(GpuRenderServerParameters {
path: p,
cache_path,
cache_size,
})
} else {
Err(argument::Error::InvalidValue {
value: s.unwrap_or("").to_string(),
expected: String::from("gpu-render-server must include 'path'"),
})
}
}
pub fn get_arguments() -> Vec<Argument> {
vec![
#[cfg(feature = "audio_cras")]
Argument::value("cras-snd",
"[capture=true,client=crosvm,socket=unified,num_output_streams=1,num_input_streams=1]",
"Comma separated key=value pairs for setting up cras snd devices.
Possible key values:
capture - Enable audio capture. Default to false.
client_type - Set specific client type for cras backend.
num_output_streams - Set number of output PCM streams
num_input_streams - Set number of input PCM streams"),
Argument::value("host_ip",
"IP",
"IP address to assign to host tap interface."),
Argument::value("tap-name",
"NAME",
"Name of a configured persistent TAP interface to use for networking. A different virtual network card will be added each time this argument is given."),
Argument::value("tap-fd",
"fd",
"File descriptor for configured tap device. A different virtual network card will be added each time this argument is given."),
Argument::value("vhost-vsock-fd", "FD", "Open FD to the vhost-vsock device, mutually exclusive with vhost-vsock-device."),
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
Argument::flag_or_value("gpu-render-server",
"[path=PATH]",
"(EXPERIMENTAL) Comma separated key=value pairs for setting up a render server for the virtio-gpu device
Possible key values:
path=PATH - The path to the render server executable.
cache-path=PATH - The path to the render server shader cache.
cache-size=SIZE - The maximum size of the shader cache."),
Argument::flag_or_value("coiommu",
"unpin_policy=POLICY,unpin_interval=NUM,unpin_limit=NUM,unpin_gen_threshold=NUM ",
"Comma separated key=value pairs for setting up coiommu devices.
Possible key values:
unpin_policy=lru - LRU unpin policy.
unpin_interval=NUM - Unpin interval time in seconds.
unpin_limit=NUM - Unpin limit for each unpin cycle, in unit of page count. 0 is invalid.
unpin_gen_threshold=NUM - Number of unpin intervals a pinned page must be busy for to be aged into the older which is less frequently checked generation."),
]
}
pub fn set_arguments(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
match name {
#[cfg(feature = "audio_cras")]
"cras-snd" => {
cfg.cras_snds.push(
value
.unwrap()
.parse()
.map_err(|e: CrasSndError| argument::Error::Syntax(e.to_string()))?,
);
}
"vhost-vsock-fd" => {
if cfg.vhost_vsock_device.is_some() {
return Err(argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("A vhost-vsock device was already specified"),
});
}
let fd: i32 = value
.unwrap()
.parse()
.map_err(|_| argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("this value for `vhost-vsock-fd` needs to be integer"),
})?;
cfg.vhost_vsock_device = Some(PathBuf::from(format!("/proc/self/fd/{}", fd)));
}
"vhost-vsock-device" => {
if cfg.vhost_vsock_device.is_some() {
return Err(argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("A vhost-vsock device was already specified"),
});
}
let vhost_vsock_device_path = PathBuf::from(value.unwrap());
if !vhost_vsock_device_path.exists() {
return Err(argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("this vhost-vsock device path does not exist"),
});
}
cfg.vhost_vsock_device = Some(vhost_vsock_device_path);
}
"shared-dir" => {
// This is formatted as multiple fields, each separated by ":". The first 2 fields are
// fixed (src:tag). The rest may appear in any order:
//
// * type=TYPE - must be one of "p9" or "fs" (default: p9)
// * uidmap=UIDMAP - a uid map in the format "inner outer count[,inner outer count]"
// (default: "0 <current euid> 1")
// * gidmap=GIDMAP - a gid map in the same format as uidmap
// (default: "0 <current egid> 1")
// * privileged_quota_uids=UIDS - Space-separated list of privileged uid values. When
// performing quota-related operations, these UIDs are treated as if they have
// CAP_FOWNER.
// * timeout=TIMEOUT - a timeout value in seconds, which indicates how long attributes
// and directory contents should be considered valid (default: 5)
// * cache=CACHE - one of "never", "always", or "auto" (default: auto)
// * writeback=BOOL - indicates whether writeback caching should be enabled (default: false)
let param = value.unwrap();
let mut components = param.split(':');
let src =
PathBuf::from(
components
.next()
.ok_or_else(|| argument::Error::InvalidValue {
value: param.to_owned(),
expected: String::from("missing source path for `shared-dir`"),
})?,
);
let tag = components
.next()
.ok_or_else(|| argument::Error::InvalidValue {
value: param.to_owned(),
expected: String::from("missing tag for `shared-dir`"),
})?
.to_owned();
if !src.is_dir() {
return Err(argument::Error::InvalidValue {
value: param.to_owned(),
expected: String::from("source path for `shared-dir` must be a directory"),
});
}
let mut shared_dir = SharedDir {
src,
tag,
..Default::default()
};
for opt in components {
let mut o = opt.splitn(2, '=');
let kind = o.next().ok_or_else(|| argument::Error::InvalidValue {
value: opt.to_owned(),
expected: String::from("`shared-dir` options must not be empty"),
})?;
let value = o.next().ok_or_else(|| argument::Error::InvalidValue {
value: opt.to_owned(),
expected: String::from("`shared-dir` options must be of the form `kind=value`"),
})?;
match kind {
"type" => {
shared_dir.kind =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`type` must be one of `fs` or `9p`"),
})?
}
"uidmap" => shared_dir.uid_map = value.into(),
"gidmap" => shared_dir.gid_map = value.into(),
#[cfg(feature = "chromeos")]
"privileged_quota_uids" => {
shared_dir.fs_cfg.privileged_quota_uids =
value.split(' ').map(|s| s.parse().unwrap()).collect();
}
"timeout" => {
let seconds = value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`timeout` must be an integer"),
})?;
let dur = Duration::from_secs(seconds);
shared_dir.fs_cfg.entry_timeout = dur;
shared_dir.fs_cfg.attr_timeout = dur;
}
"cache" => {
let policy = value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from(
"`cache` must be one of `never`, `always`, or `auto`",
),
})?;
shared_dir.fs_cfg.cache_policy = policy;
}
"writeback" => {
let writeback =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`writeback` must be a boolean"),
})?;
shared_dir.fs_cfg.writeback = writeback;
}
"rewrite-security-xattrs" => {
let rewrite_security_xattrs =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from(
"`rewrite-security-xattrs` must be a boolean",
),
})?;
shared_dir.fs_cfg.rewrite_security_xattrs = rewrite_security_xattrs;
}
"ascii_casefold" => {
let ascii_casefold =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`ascii_casefold` must be a boolean"),
})?;
shared_dir.fs_cfg.ascii_casefold = ascii_casefold;
shared_dir.p9_cfg.ascii_casefold = ascii_casefold;
}
"dax" => {
let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`dax` must be a boolean"),
})?;
shared_dir.fs_cfg.use_dax = use_dax;
}
"posix_acl" => {
let posix_acl =
value.parse().map_err(|_| argument::Error::InvalidValue {
value: value.to_owned(),
expected: String::from("`posix_acl` must be a boolean"),
})?;
shared_dir.fs_cfg.posix_acl = posix_acl;
}
_ => {
return Err(argument::Error::InvalidValue {
value: kind.to_owned(),
expected: String::from("unrecognized option for `shared-dir`"),
})
}
}
}
cfg.shared_dirs.push(shared_dir);
}
"host_ip" => {
if cfg.host_ip.is_some() {
return Err(argument::Error::TooManyArguments(
"`host_ip` already given".to_owned(),
));
}
cfg.host_ip =
Some(
value
.unwrap()
.parse()
.map_err(|_| argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from("`host_ip` needs to be in the form \"x.x.x.x\""),
})?,
)
}
"tap-name" => {
cfg.tap_name.push(value.unwrap().to_owned());
}
"tap-fd" => {
cfg.tap_fd.push(
value
.unwrap()
.parse()
.map_err(|_| argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: String::from(
"this value for `tap-fd` must be an unsigned integer",
),
})?,
);
}
"coiommu" => {
let mut params: devices::CoIommuParameters = Default::default();
if let Some(v) = value {
let opts = v
.split(',')
.map(|frag| frag.splitn(2, '='))
.map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
for (k, v) in opts {
match k {
"unpin_policy" => {
params.unpin_policy = v
.parse::<devices::CoIommuUnpinPolicy>()
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
}
"unpin_interval" => {
params.unpin_interval =
Duration::from_secs(v.parse::<u64>().map_err(|e| {
argument::Error::UnknownArgument(format!("{}", e))
})?)
}
"unpin_limit" => {
let limit = v
.parse::<u64>()
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?;
if limit == 0 {
return Err(argument::Error::InvalidValue {
value: v.to_owned(),
expected: String::from("Please use non-zero unpin_limit value"),
});
}
params.unpin_limit = Some(limit)
}
"unpin_gen_threshold" => {
params.unpin_gen_threshold = v
.parse::<u64>()
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
}
_ => {
return Err(argument::Error::UnknownArgument(format!(
"coiommu parameter {}",
k
)));
}
}
}
}
if cfg.coiommu_param.is_some() {
return Err(argument::Error::TooManyArguments(
"coiommu param already given".to_owned(),
));
}
cfg.coiommu_param = Some(params);
}
#[cfg(all(feature = "gpu", feature = "virgl_renderer_next"))]
"gpu-render-server" => {
cfg.gpu_render_server_parameters = Some(parse_gpu_render_server_options(value)?);
}
_ => unreachable!(),
}
Ok(())
}