Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion crates/muvm/src/bin/muvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use muvm::cli_options::options;
use muvm::cpu::{get_fallback_cores, get_performance_cores};
use muvm::env::{find_muvm_exec, prepare_env_vars};
use muvm::hidpipe_server::spawn_hidpipe_server;
use muvm::launch::{launch_or_lock, LaunchResult};
use muvm::launch::{launch_or_lock, LaunchResult, DYNAMIC_PORT_RANGE};
use muvm::monitor::spawn_monitor;
use muvm::net::{connect_to_passt, start_passt};
use muvm::types::MiB;
Expand Down Expand Up @@ -74,6 +74,7 @@ fn main() -> Result<()> {
options.command,
options.command_args,
options.env,
options.interactive,
)? {
LaunchResult::LaunchRequested => {
// There was a muvm instance already running and we've requested it
Expand Down Expand Up @@ -297,6 +298,7 @@ fn main() -> Result<()> {
return Err(err).context("Failed to configure vsock for pulse socket");
}
}

let hidpipe_path = Path::new(&run_path).join("hidpipe");
spawn_hidpipe_server(hidpipe_path.clone()).context("Failed to spawn hidpipe thread")?;
let hidpipe_path = CString::new(
Expand All @@ -312,6 +314,25 @@ fn main() -> Result<()> {
let err = Errno::from_raw_os_error(-err);
return Err(err).context("Failed to configure vsock for hidpipe socket");
}

let socket_dir = Path::new(&run_path).join("krun/socket");
std::fs::create_dir_all(&socket_dir)?;
// Dynamic ports: Applications may listen on these sockets as neeeded.
for port in DYNAMIC_PORT_RANGE {
let socket_path = socket_dir.join(format!("port-{}", port));
let socket_path = CString::new(
socket_path
.to_str()
.expect("socket_path should not contain invalid UTF-8"),
)
.context("Failed to process dynamic socket path as it contains NUL character")?;
// SAFETY: `socket_path` is a pointer to a `CString` with long enough lifetime.
let err = unsafe { krun_add_vsock_port(ctx_id, port, socket_path.as_ptr()) };
if err < 0 {
let err = Errno::from_raw_os_error(-err);
return Err(err).context("Failed to configure vsock for dynamic socket");
}
}
}

// Forward the native X11 display into the guest as a socket
Expand Down
7 changes: 6 additions & 1 deletion crates/muvm/src/cli_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Options {
pub server_port: u32,
pub fex_images: Vec<String>,
pub direct_x11: bool,
pub interactive: bool,
pub command: PathBuf,
pub command_args: Vec<String>,
}
Expand Down Expand Up @@ -116,7 +117,10 @@ pub fn options() -> OptionParser<Options> {
"--direct-x11 requires the `x11bridge` crate feature",
)
.hide();

let interactive = long("interactive")
.short('i')
.help("Allocate a tty guest-side and connect it to the current stdin/out")
.switch();
let command = positional("COMMAND").help("the command you want to execute in the vm");
let command_args = any::<String, _, _>("COMMAND_ARGS", |arg| {
(!["--help", "-h"].contains(&&*arg)).then_some(arg)
Expand All @@ -134,6 +138,7 @@ pub fn options() -> OptionParser<Options> {
server_port,
fex_images,
direct_x11,
interactive,
// positionals
command,
command_args,
Expand Down
72 changes: 70 additions & 2 deletions crates/muvm/src/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ use anyhow::{anyhow, Context, Result};
use rustix::fs::{flock, FlockOperation};
use uuid::Uuid;

use super::utils::env::find_in_path;
use crate::env::prepare_env_vars;
use crate::utils::launch::Launch;
use rustix::path::Arg;
use std::ops::Range;
use std::process::{Child, Command};

pub const DYNAMIC_PORT_RANGE: Range<u32> = 50000..50200;

pub enum LaunchResult {
LaunchRequested,
Expand Down Expand Up @@ -50,18 +56,79 @@ impl Display for LaunchError {
}
}

fn start_socat() -> Result<(Child, u32)> {
let run_path = env::var("XDG_RUNTIME_DIR")
.map_err(|e| anyhow!("unable to get XDG_RUNTIME_DIR: {:?}", e))?;
let socket_dir = Path::new(&run_path).join("krun/socket");
let socat_path =
find_in_path("socat")?.ok_or_else(|| anyhow!("Unable to find socat in PATH"))?;
for port in DYNAMIC_PORT_RANGE {
let path = socket_dir.join(format!("port-{}", port));
if path.exists() {
continue;
}
let child = Command::new(&socat_path)
.arg(format!("unix-l:{}", path.as_os_str().to_string_lossy()))
.arg("-,raw,echo=0")
.spawn()?;
return Ok((child, port));
}
Err(anyhow!("Ran out of ports."))
}

fn escape_for_socat(s: String) -> String {
let mut ret = String::with_capacity(s.len());
for c in s.chars() {
match c {
':' | ',' | '!' | '"' | '\'' | '\\' | '(' | '[' | '{' => {
ret.push('\\');
},
_ => {},
}
ret.push(c);
}
ret
}

fn wrapped_launch(
server_port: u32,
cookie: Uuid,
mut command: PathBuf,
mut command_args: Vec<String>,
env: HashMap<String, String>,
interactive: bool,
) -> Result<()> {
if !interactive {
return request_launch(server_port, cookie, command, command_args, env);
}
let (mut socat, vsock_port) = start_socat()?;
command_args.insert(0, command.to_string_lossy().into_owned());
command_args = vec![
format!("vsock:2:{}", vsock_port),
format!(
"exec:{},pty,setsid,stderr",
escape_for_socat(command_args.join(" "))
),
];
command = "socat".into();
request_launch(server_port, cookie, command, command_args, env)?;
socat.wait()?;
Ok(())
}

pub fn launch_or_lock(
server_port: u32,
command: PathBuf,
command_args: Vec<String>,
env: Vec<(String, Option<String>)>,
interactive: bool,
) -> Result<LaunchResult> {
let running_server_port = env::var("MUVM_SERVER_PORT").ok();
if let Some(port) = running_server_port {
let port: u32 = port.parse()?;
let env = prepare_env_vars(env)?;
let cookie = read_cookie()?;
if let Err(err) = request_launch(port, cookie, command, command_args, env) {
if let Err(err) = wrapped_launch(port, cookie, command, command_args, env, interactive) {
return Err(anyhow!("could not request launch to server: {err}"));
}
return Ok(LaunchResult::LaunchRequested);
Expand All @@ -80,12 +147,13 @@ pub fn launch_or_lock(
let env = prepare_env_vars(env)?;
let mut tries = 0;
loop {
match request_launch(
match wrapped_launch(
server_port,
cookie,
command.clone(),
command_args.clone(),
env.clone(),
interactive,
) {
Err(err) => match err.downcast_ref::<LaunchError>() {
Some(&LaunchError::Connection(_)) => {
Expand Down