From 6604ebdc667fd915c46fe71eb0101ee1a68850a8 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Thu, 23 Oct 2025 20:12:38 -0300 Subject: [PATCH 1/7] Add a flag to run a custom init instead of muvm-guest Allow the user to replace muvm-guest with a custom init process. With updated libkrun, it will even run as PID 1, making it possible to run systemd. Of course, this is not the suggested way to use muvm, but some use cases necesitate the use of systemd. The next few commits will facilitate running individual parts of muvm-guest as separate processes under a custom service manager. Signed-off-by: Val Packett --- crates/muvm/src/bin/muvm.rs | 28 ++++++++++++++++++++-------- crates/muvm/src/cli_options.rs | 9 +++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/crates/muvm/src/bin/muvm.rs b/crates/muvm/src/bin/muvm.rs index 0827e8db..64608d1b 100644 --- a/crates/muvm/src/bin/muvm.rs +++ b/crates/muvm/src/bin/muvm.rs @@ -429,13 +429,21 @@ fn main() -> Result { .to_str() .context("Temporary directory path contains invalid UTF-8")? .to_owned(); - let muvm_guest_args = vec![ - muvm_guest_path - .to_str() - .context("Failed to process `muvm-guest` path as it contains invalid UTF-8")? - .to_owned(), - muvm_config_path, - ]; + let custom_init = options.custom_init_cmdline.is_some(); + let muvm_guest_args = if let Some(cmdline) = options.custom_init_cmdline { + cmdline + .split_whitespace() + .map(|a| a.to_owned()) + .collect::>() + } else { + vec![ + muvm_guest_path + .to_str() + .context("Failed to process `muvm-guest` path as it contains invalid UTF-8")? + .to_owned(), + muvm_config_path, + ] + }; // And forward XAUTHORITY. This will be modified to fix the // display name in muvm-guest. @@ -470,7 +478,11 @@ fn main() -> Result { let krun_config_env = CString::new(format!("KRUN_CONFIG={}", config_file.path().display())) .context("Failed to process config_file var as it contains NUL character")?; - let env: Vec<*const c_char> = vec![krun_config_env.as_ptr(), std::ptr::null()]; + let mut env: Vec<*const c_char> = vec![krun_config_env.as_ptr()]; + if custom_init { + env.push(c"KRUN_INIT_PID1=1".as_ptr()); + } + env.push(std::ptr::null()); { // Sets environment variables to be configured in the context of the executable. diff --git a/crates/muvm/src/cli_options.rs b/crates/muvm/src/cli_options.rs index 5080c911..69326da0 100644 --- a/crates/muvm/src/cli_options.rs +++ b/crates/muvm/src/cli_options.rs @@ -47,6 +47,7 @@ pub struct Options { pub emulator: Option, pub init_commands: Vec, pub user_init_commands: Vec, + pub custom_init_cmdline: Option, pub command: PathBuf, pub command_args: Vec, } @@ -179,6 +180,13 @@ pub fn options() -> OptionParser { ) .argument("COMMAND") .many(); + let custom_init_cmdline = long("custom-init-cmdline") + .help( + "Command and arguments to run as PID 1, replacing muvm's own init. + (Warning: this will break many muvm features, unless your init reimplements them.)", + ) + .argument("CMDLINE") + .optional(); let command = positional("COMMAND").help("the command you want to execute in the vm"); let command_args = any::("COMMAND_ARGS", |arg| { (!["--help", "-h"].contains(&&*arg)).then_some(arg) @@ -202,6 +210,7 @@ pub fn options() -> OptionParser { emulator, init_commands, user_init_commands, + custom_init_cmdline, // positionals command, command_args, From b16d966e611009417e49716bd3e7b21132e44333 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Thu, 23 Oct 2025 20:55:08 -0300 Subject: [PATCH 2/7] muvm-guest: Add support for running as muvm-remote and muvm-configure-network To better support running under a custom service manager (e.g. systemd), allow running individual functions of the guest binary when called with distinct binary names. Signed-off-by: Val Packett --- crates/muvm/src/guest/bin/muvm-guest.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/muvm/src/guest/bin/muvm-guest.rs b/crates/muvm/src/guest/bin/muvm-guest.rs index 6bac1838..075a6ab0 100644 --- a/crates/muvm/src/guest/bin/muvm-guest.rs +++ b/crates/muvm/src/guest/bin/muvm-guest.rs @@ -2,6 +2,7 @@ use std::fs::File; use std::io::Read; use std::os::fd::AsFd; use std::panic::catch_unwind; +use std::path::PathBuf; use std::process::{Command, ExitCode}; use std::{cmp, env, fs, thread}; @@ -27,6 +28,19 @@ const KRUN_CONFIG: &str = "KRUN_CONFIG"; fn main() -> Result { env_logger::init(); + let binary_path = env::args().next().context("arg0")?; + let bb = binary_path.split('/').next_back().context("arg0 split")?; + match bb { + "muvm-configure-network" => return configure_network(), + "muvm-remote" => { + let rt = tokio::runtime::Runtime::new().unwrap(); + let mut command_args = env::args().skip(1); + let command = command_args.next().context("command name")?; + return rt.block_on(server_main(PathBuf::from(command), command_args.collect())); + }, + _ => { /* continue with all-in-one mode */ }, + } + if let Ok(val) = env::var("__X11BRIDGE_DEBUG") { start_x11bridge(val.parse()?); return Ok(ExitCode::SUCCESS); From 279f236c951b2b940bfb67b64e45fdcded48a149 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 24 Oct 2025 01:07:32 -0300 Subject: [PATCH 3/7] muvm-guest: Add support for running as muvm-pwbridge Support running the PipeWire bridge as a separate process, optionally with systemd socket activation. Signed-off-by: Val Packett --- Cargo.lock | 34 ++++++++++++++++++++++++ crates/muvm/Cargo.toml | 1 + crates/muvm/src/guest/bin/muvm-guest.rs | 9 +++++-- crates/muvm/src/guest/bridge/common.rs | 18 +++++++++++-- crates/muvm/src/guest/bridge/pipewire.rs | 11 +++----- 5 files changed, 62 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 262cefba..ac3d7d0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,6 +494,17 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "listenfd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87bc54a4629b4294d0b3ef041b64c40c611097a677d9dc07b2c67739fe39dba" +dependencies = [ + "libc", + "uuid", + "winapi", +] + [[package]] name = "lock_api" version = "0.4.12" @@ -563,6 +574,7 @@ dependencies = [ "input-linux", "input-linux-sys", "krun-sys", + "listenfd", "log", "neli", "nix 0.30.1", @@ -1044,6 +1056,28 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/crates/muvm/Cargo.toml b/crates/muvm/Cargo.toml index df87995d..583b47ae 100644 --- a/crates/muvm/Cargo.toml +++ b/crates/muvm/Cargo.toml @@ -29,6 +29,7 @@ tokio = { version = "1.38.0", default-features = false, features = ["io-util", " tokio-stream = { version = "0.1.15", default-features = false, features = ["net", "sync"] } udev = { version = "0.9.0", default-features = false, features = [] } uuid = { version = "1.10.0", default-features = false, features = ["serde", "std", "v7"] } +listenfd = "1.0.2" [[bin]] name = "muvm" diff --git a/crates/muvm/src/guest/bin/muvm-guest.rs b/crates/muvm/src/guest/bin/muvm-guest.rs index 075a6ab0..73044826 100644 --- a/crates/muvm/src/guest/bin/muvm-guest.rs +++ b/crates/muvm/src/guest/bin/muvm-guest.rs @@ -8,7 +8,8 @@ use std::{cmp, env, fs, thread}; use anyhow::{anyhow, Context, Result}; use muvm::guest::box64::setup_box; -use muvm::guest::bridge::pipewire::start_pwbridge; +use muvm::guest::bridge::common::{bridge_loop, bridge_loop_with_listenfd}; +use muvm::guest::bridge::pipewire::{pipewire_sock_path, PipeWireProtocolHandler}; use muvm::guest::bridge::x11::start_x11bridge; use muvm::guest::fex::setup_fex; use muvm::guest::hidpipe::start_hidpipe; @@ -32,6 +33,10 @@ fn main() -> Result { let bb = binary_path.split('/').next_back().context("arg0 split")?; match bb { "muvm-configure-network" => return configure_network(), + "muvm-pwbridge" => { + bridge_loop_with_listenfd::(pipewire_sock_path); + return Ok(()); + }, "muvm-remote" => { let rt = tokio::runtime::Runtime::new().unwrap(); let mut command_args = env::args().skip(1); @@ -169,7 +174,7 @@ fn main() -> Result { }); thread::spawn(|| { - if catch_unwind(start_pwbridge).is_err() { + if catch_unwind(|| bridge_loop::(&pipewire_sock_path())).is_err() { eprintln!("pwbridge thread crashed, pipewire passthrough will no longer function"); } }); diff --git a/crates/muvm/src/guest/bridge/common.rs b/crates/muvm/src/guest/bridge/common.rs index 7cc54a8c..e96b2abd 100644 --- a/crates/muvm/src/guest/bridge/common.rs +++ b/crates/muvm/src/guest/bridge/common.rs @@ -960,10 +960,24 @@ impl<'a, T: ProtocolHandler> SubPoll<'a, T> { } } +pub fn bridge_loop_with_listenfd(fallback_sock_path: impl Fn() -> String) { + if let Some(listen_sock) = listenfd::ListenFd::from_env() + .take_unix_listener(0) + .unwrap() + { + bridge_loop_sock::(listen_sock) + } else { + bridge_loop::(&fallback_sock_path()) + } +} + pub fn bridge_loop(sock_path: &str) { - let epoll = Epoll::new(EpollCreateFlags::empty()).unwrap(); _ = fs::remove_file(sock_path); - let listen_sock = UnixListener::bind(sock_path).unwrap(); + bridge_loop_sock::(UnixListener::bind(sock_path).unwrap()); +} + +pub fn bridge_loop_sock(listen_sock: UnixListener) { + let epoll = Epoll::new(EpollCreateFlags::empty()).unwrap(); epoll .add( &listen_sock, diff --git a/crates/muvm/src/guest/bridge/pipewire.rs b/crates/muvm/src/guest/bridge/pipewire.rs index 343dd211..005f8a25 100644 --- a/crates/muvm/src/guest/bridge/pipewire.rs +++ b/crates/muvm/src/guest/bridge/pipewire.rs @@ -9,7 +9,6 @@ use nix::errno::Errno; use nix::sys::epoll::EpollFlags; use nix::sys::eventfd::{EfdFlags, EventFd}; -use crate::guest::bridge::common; use crate::guest::bridge::common::{ Client, CrossDomainHeader, CrossDomainResource, MessageResourceFinalizer, ProtocolHandler, StreamRecvResult, StreamSendResult, @@ -165,7 +164,7 @@ impl PipeWireHeader { } } -struct PipeWireResourceFinalizer; +pub struct PipeWireResourceFinalizer; impl MessageResourceFinalizer for PipeWireResourceFinalizer { type Handler = PipeWireProtocolHandler; @@ -194,7 +193,7 @@ impl ClientNodeData { } } -struct PipeWireProtocolHandler { +pub struct PipeWireProtocolHandler { client_nodes: HashMap, guest_to_host_eventfds: HashMap, host_to_guest_eventfds: HashMap, @@ -405,8 +404,6 @@ impl ProtocolHandler for PipeWireProtocolHandler { } } -pub fn start_pwbridge() { - let sock_path = format!("{}/pipewire-0", env::var("XDG_RUNTIME_DIR").unwrap()); - - common::bridge_loop::(&sock_path) +pub fn pipewire_sock_path() -> String { + format!("{}/pipewire-0", env::var("XDG_RUNTIME_DIR").unwrap()) } From 2478d872735c0273dd7c57c3a943a028e12b6b60 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 21 Nov 2025 03:38:48 -0300 Subject: [PATCH 4/7] x11: use get_or_init instead of the set/get dance for the offset This is how OnceLock is really meant to be used.. This will be helpful for the custom init support, where various ways of starting the bridge loop will be used. Signed-off-by: Val Packett --- crates/muvm/src/guest/bridge/x11.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/muvm/src/guest/bridge/x11.rs b/crates/muvm/src/guest/bridge/x11.rs index 0bc4616b..150ed8ea 100644 --- a/crates/muvm/src/guest/bridge/x11.rs +++ b/crates/muvm/src/guest/bridge/x11.rs @@ -4,7 +4,6 @@ use std::ffi::{c_long, c_void, CString}; use std::fs::{read_to_string, remove_file, File}; use std::io::{IoSlice, Write}; use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd}; -use std::process::exit; use std::ptr::NonNull; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, OnceLock}; @@ -704,7 +703,7 @@ impl RemoteCaller { // Find the vDSO and the address of a syscall instruction within it let (vdso_start, _) = find_vdso(Some(pid))?; - let syscall_addr = vdso_start + SYSCALL_OFFSET.get().unwrap(); + let syscall_addr = vdso_start + SYSCALL_OFFSET.get_or_init(find_syscall_offset); let mut regs = old_regs; arch::set_syscall_addr(&mut regs, syscall_addr); @@ -847,9 +846,7 @@ fn find_vdso(pid: Option) -> Result<(usize, usize), Errno> { Err(Errno::EINVAL) } -pub fn start_x11bridge(display: u32) { - let sock_path = format!("/tmp/.X11-unix/X{display}"); - +fn find_syscall_offset() -> usize { // Look for a syscall instruction in the vDSO. We assume all processes map // the same vDSO (which should be true if they are running under the same // kernel!) @@ -858,14 +855,13 @@ pub fn start_x11bridge(display: u32) { let addr = vdso_start + off; let val = unsafe { std::ptr::read(addr as *const arch::SyscallInstr) }; if val == arch::SYSCALL_INSTR { - SYSCALL_OFFSET.set(off).unwrap(); - break; + return off; } } - if SYSCALL_OFFSET.get().is_none() { - eprintln!("Failed to find syscall instruction in vDSO"); - exit(1); - } + panic!("Failed to find syscall instruction in vDSO"); +} +pub fn start_x11bridge(display: u32) { + let sock_path = format!("/tmp/.X11-unix/X{display}"); common::bridge_loop::(&sock_path) } From 287b90a465987fd0173db334a7a65a113813f716 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 21 Nov 2025 03:41:13 -0300 Subject: [PATCH 5/7] muvm-guest: add support for running as muvm-x11bridge Signed-off-by: Val Packett --- crates/muvm/src/guest/bin/muvm-guest.rs | 6 +++++- crates/muvm/src/guest/bridge/x11.rs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/muvm/src/guest/bin/muvm-guest.rs b/crates/muvm/src/guest/bin/muvm-guest.rs index 73044826..de029ece 100644 --- a/crates/muvm/src/guest/bin/muvm-guest.rs +++ b/crates/muvm/src/guest/bin/muvm-guest.rs @@ -10,7 +10,7 @@ use anyhow::{anyhow, Context, Result}; use muvm::guest::box64::setup_box; use muvm::guest::bridge::common::{bridge_loop, bridge_loop_with_listenfd}; use muvm::guest::bridge::pipewire::{pipewire_sock_path, PipeWireProtocolHandler}; -use muvm::guest::bridge::x11::start_x11bridge; +use muvm::guest::bridge::x11::{start_x11bridge, X11ProtocolHandler}; use muvm::guest::fex::setup_fex; use muvm::guest::hidpipe::start_hidpipe; use muvm::guest::mount::mount_filesystems; @@ -37,6 +37,10 @@ fn main() -> Result { bridge_loop_with_listenfd::(pipewire_sock_path); return Ok(()); }, + "muvm-x11bridge" => { + bridge_loop_with_listenfd::(|| "/tmp/.X11-unix/X1".to_owned()); + return Ok(ExitCode::SUCCESS); + }, "muvm-remote" => { let rt = tokio::runtime::Runtime::new().unwrap(); let mut command_args = env::args().skip(1); diff --git a/crates/muvm/src/guest/bridge/x11.rs b/crates/muvm/src/guest/bridge/x11.rs index 150ed8ea..62cc64e5 100644 --- a/crates/muvm/src/guest/bridge/x11.rs +++ b/crates/muvm/src/guest/bridge/x11.rs @@ -155,7 +155,7 @@ struct CrossDomainFutexDestroy { pad: u32, } -enum X11ResourceFinalizer { +pub enum X11ResourceFinalizer { Gem(GemHandleFinalizer), Futex(u32), } @@ -186,7 +186,7 @@ impl MessageResourceFinalizer for X11ResourceFinalizer { } } -struct X11ProtocolHandler { +pub struct X11ProtocolHandler { // futex_watchers gets dropped first futex_watchers: HashMap, got_first_req: bool, From 024a16b7886b4bf781e5bfe0e4fe88b0a2685217 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 21 Nov 2025 03:54:18 -0300 Subject: [PATCH 6/7] muvm-guest: add support for running as muvm-hidpipe Signed-off-by: Val Packett --- crates/muvm/src/guest/bin/muvm-guest.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/muvm/src/guest/bin/muvm-guest.rs b/crates/muvm/src/guest/bin/muvm-guest.rs index de029ece..94291f3f 100644 --- a/crates/muvm/src/guest/bin/muvm-guest.rs +++ b/crates/muvm/src/guest/bin/muvm-guest.rs @@ -41,6 +41,14 @@ fn main() -> Result { bridge_loop_with_listenfd::(|| "/tmp/.X11-unix/X1".to_owned()); return Ok(ExitCode::SUCCESS); }, + "muvm-hidpipe" => { + let config_path = + env::var("MUVM_REMOTE_CONFIG").context("expected MUVM_REMOTE_CONFIG to be set")?; + let options = parse_config(config_path)?; + let uid = options.uid; + start_hidpipe(uid); + return Ok(ExitCode::SUCCESS); + }, "muvm-remote" => { let rt = tokio::runtime::Runtime::new().unwrap(); let mut command_args = env::args().skip(1); From 0a0499003c03eb6f3cf271e00da6ee0ec00546ca Mon Sep 17 00:00:00 2001 From: Val Packett Date: Fri, 31 Oct 2025 04:37:38 -0300 Subject: [PATCH 7/7] muvm-guest: pass config to split remote process Signed-off-by: Val Packett --- crates/muvm/src/bin/muvm.rs | 10 ++++++ crates/muvm/src/guest/bin/muvm-guest.rs | 45 ++++++++++++++----------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/crates/muvm/src/bin/muvm.rs b/crates/muvm/src/bin/muvm.rs index 64608d1b..d839c639 100644 --- a/crates/muvm/src/bin/muvm.rs +++ b/crates/muvm/src/bin/muvm.rs @@ -478,9 +478,19 @@ fn main() -> Result { let krun_config_env = CString::new(format!("KRUN_CONFIG={}", config_file.path().display())) .context("Failed to process config_file var as it contains NUL character")?; + #[allow(unused_assignments)] // wat? + let mut muvm_config_env = None; // keep in this scope let mut env: Vec<*const c_char> = vec![krun_config_env.as_ptr()]; if custom_init { env.push(c"KRUN_INIT_PID1=1".as_ptr()); + muvm_config_env = Some( + CString::new(format!( + "MUVM_REMOTE_CONFIG={}", + muvm_config_file.path().display() + )) + .context("Failed to process internal config path as it contains NUL character")?, + ); + env.push(muvm_config_env.as_ref().unwrap().as_ptr()); } env.push(std::ptr::null()); diff --git a/crates/muvm/src/guest/bin/muvm-guest.rs b/crates/muvm/src/guest/bin/muvm-guest.rs index 94291f3f..757780d9 100644 --- a/crates/muvm/src/guest/bin/muvm-guest.rs +++ b/crates/muvm/src/guest/bin/muvm-guest.rs @@ -2,7 +2,6 @@ use std::fs::File; use std::io::Read; use std::os::fd::AsFd; use std::panic::catch_unwind; -use std::path::PathBuf; use std::process::{Command, ExitCode}; use std::{cmp, env, fs, thread}; @@ -26,16 +25,32 @@ use rustix::process::{getrlimit, setrlimit, Resource}; const KRUN_CONFIG: &str = "KRUN_CONFIG"; +fn parse_config(config_path: String) -> Result { + let mut config_file = File::open(&config_path)?; + let mut config_buf = Vec::new(); + config_file.read_to_end(&mut config_buf)?; + fs::remove_file(config_path).context("Unable to delete temporary muvm configuration file")?; + if let Ok(krun_config_path) = env::var(KRUN_CONFIG) { + fs::remove_file(krun_config_path) + .context("Unable to delete temporary krun configuration file")?; + // SAFETY: We are single-threaded at this point + env::remove_var(KRUN_CONFIG); + } + // SAFETY: We are single-threaded at this point + env::remove_var("KRUN_WORKDIR"); + Ok(serde_json::from_slice::(&config_buf)?) +} + fn main() -> Result { env_logger::init(); let binary_path = env::args().next().context("arg0")?; let bb = binary_path.split('/').next_back().context("arg0 split")?; match bb { - "muvm-configure-network" => return configure_network(), + "muvm-configure-network" => return configure_network().map(|()| ExitCode::SUCCESS), "muvm-pwbridge" => { bridge_loop_with_listenfd::(pipewire_sock_path); - return Ok(()); + return Ok(ExitCode::SUCCESS); }, "muvm-x11bridge" => { bridge_loop_with_listenfd::(|| "/tmp/.X11-unix/X1".to_owned()); @@ -51,9 +66,13 @@ fn main() -> Result { }, "muvm-remote" => { let rt = tokio::runtime::Runtime::new().unwrap(); - let mut command_args = env::args().skip(1); - let command = command_args.next().context("command name")?; - return rt.block_on(server_main(PathBuf::from(command), command_args.collect())); + let config_path = + env::var("MUVM_REMOTE_CONFIG").context("expected MUVM_REMOTE_CONFIG to be set")?; + let options = parse_config(config_path)?; + return rt.block_on(server_main( + options.command.command, + options.command.command_args, + )); }, _ => { /* continue with all-in-one mode */ }, } @@ -66,19 +85,7 @@ fn main() -> Result { let config_path = env::args() .nth(1) .context("expected configuration file path")?; - let mut config_file = File::open(&config_path)?; - let mut config_buf = Vec::new(); - config_file.read_to_end(&mut config_buf)?; - fs::remove_file(config_path).context("Unable to delete temporary muvm configuration file")?; - if let Ok(krun_config_path) = env::var(KRUN_CONFIG) { - fs::remove_file(krun_config_path) - .context("Unable to delete temporary krun configuration file")?; - // SAFETY: We are single-threaded at this point - env::remove_var(KRUN_CONFIG); - } - // SAFETY: We are single-threaded at this point - env::remove_var("KRUN_WORKDIR"); - let options = serde_json::from_slice::(&config_buf)?; + let options = parse_config(config_path)?; { const ESYNC_RLIMIT_NOFILE: u64 = 524288;