diff --git a/Cargo.lock b/Cargo.lock index f1d1f12a..784506bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,6 +576,7 @@ dependencies = [ "rustix 0.38.34", "serde", "serde_json", + "shell-words", "tempfile", "tokio", "tokio-stream", @@ -897,6 +898,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" + [[package]] name = "shlex" version = "1.3.0" diff --git a/crates/muvm/Cargo.toml b/crates/muvm/Cargo.toml index f011bca8..60a1d733 100644 --- a/crates/muvm/Cargo.toml +++ b/crates/muvm/Cargo.toml @@ -24,6 +24,7 @@ procfs = { version = "0.18.0", default-features = false, features = [] } rustix = { version = "0.38.34", default-features = false, features = ["fs", "mount", "process", "pty", "std", "stdio", "system", "termios", "use-libc-auxv"] } serde = { version = "1.0.203", default-features = false, features = ["derive"] } serde_json = { version = "1.0.117", default-features = false, features = ["std"] } +shell-words = { version = "1.1.1", default-features = false, features = ["std"] } tempfile = { version = "3.10.1", default-features = false, features = [] } tokio = { version = "1.38.0", default-features = false, features = ["io-util", "macros", "net", "process", "rt-multi-thread", "sync"] } tokio-stream = { version = "0.1.15", default-features = false, features = ["net", "sync"] } diff --git a/crates/muvm/src/bin/muvm.rs b/crates/muvm/src/bin/muvm.rs index 0827e8db..1c9fe28d 100644 --- a/crates/muvm/src/bin/muvm.rs +++ b/crates/muvm/src/bin/muvm.rs @@ -278,7 +278,7 @@ fn main() -> Result { .context("Failed to connect to `passt`")? .into() } else { - start_passt(&options.publish_ports) + start_passt(&options.publish_ports, &options.passt_args) .context("Failed to start `passt`")? .into() }; diff --git a/crates/muvm/src/cli_options.rs b/crates/muvm/src/cli_options.rs index 5080c911..fc3ccf74 100644 --- a/crates/muvm/src/cli_options.rs +++ b/crates/muvm/src/cli_options.rs @@ -37,6 +37,7 @@ pub struct Options { pub mem: Option, pub vram: Option, pub passt_socket: Option, + pub passt_args: Vec, pub fex_images: Vec, pub merged_rootfs: bool, pub interactive: bool, @@ -135,6 +136,15 @@ pub fn options() -> OptionParser { .help("Instead of starting passt, connect to passt socket at PATH") .argument("PATH") .optional(); + let passt_args = long("passt-args") + .help( + "When starting passt, append the given arguments. + May contain shell-quoted args, like so: --passt-args '-l \"my log file.txt\"'", + ) + .argument::("ARGS") + .parse(|s| shell_words::split(&s)) + .many() + .map(|nested| nested.into_iter().flatten().collect()); let interactive = long("interactive") .short('i') .help("Attach to the command's stdin/out after starting it") @@ -192,6 +202,7 @@ pub fn options() -> OptionParser { mem, vram, passt_socket, + passt_args, fex_images, merged_rootfs, interactive, diff --git a/crates/muvm/src/net.rs b/crates/muvm/src/net.rs index 8543e1c1..bc6323d4 100644 --- a/crates/muvm/src/net.rs +++ b/crates/muvm/src/net.rs @@ -82,7 +82,7 @@ where Ok(UnixStream::connect(passt_socket_path)?) } -pub fn start_passt(publish_ports: &[String]) -> Result { +pub fn start_passt(publish_ports: &[String], passt_args: &[String]) -> Result { // SAFETY: The child process should not inherit the file descriptor of // `parent_socket`. There is no documented guarantee of this, but the // implementation as of writing atomically sets `SOCK_CLOEXEC`. @@ -112,6 +112,9 @@ pub fn start_passt(publish_ports: &[String]) -> Result { for spec in publish_ports { cmd.args(PublishSpec::parse(spec)?.to_args()); } + for arg in passt_args { + cmd.arg(arg); + } let child = cmd.spawn(); if let Err(err) = child { return Err(err).context("Failed to execute `passt` as child process");