Skip to content

Commit 720da8f

Browse files
committed
Start a privileged muvm-server
Being able to run commands as root comes handy to debug the guest and will also be used to tune the virtual memory parameters on demand. Signed-off-by: Sergio Lopez <[email protected]>
1 parent 63d6e65 commit 720da8f

5 files changed

Lines changed: 47 additions & 11 deletions

File tree

crates/muvm/src/bin/muvm.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ fn main() -> Result<()> {
254254
.context("Failed to connect to `passt`")?
255255
.into()
256256
} else {
257-
start_passt(options.server_port)
257+
start_passt(options.server_port, options.root_server_port)
258258
.context("Failed to start `passt`")?
259259
.into()
260260
};
@@ -375,6 +375,10 @@ fn main() -> Result<()> {
375375
"MUVM_SERVER_PORT".to_owned(),
376376
options.server_port.to_string(),
377377
);
378+
env.insert(
379+
"MUVM_ROOT_SERVER_PORT".to_owned(),
380+
options.root_server_port.to_string(),
381+
);
378382
env.insert("MUVM_SERVER_COOKIE".to_owned(), cookie.to_string());
379383

380384
if options.direct_x11 {

crates/muvm/src/cli_options.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub struct Options {
1313
pub mem: Option<MiB>,
1414
pub vram: Option<MiB>,
1515
pub passt_socket: Option<PathBuf>,
16+
pub root_server_port: u32,
1617
pub server_port: u32,
1718
pub fex_images: Vec<String>,
1819
pub direct_x11: bool,
@@ -93,6 +94,12 @@ pub fn options() -> OptionParser<Options> {
9394
.help("Instead of starting passt, connect to passt socket at PATH")
9495
.argument("PATH")
9596
.optional();
97+
let root_server_port = long("root-server-port")
98+
.short('r')
99+
.help("Set the port to be used in root server mode")
100+
.argument("ROOT_SERVER_PORT")
101+
.fallback(3335)
102+
.display_fallback();
96103
let server_port = long("server-port")
97104
.short('p')
98105
.help("Set the port to be used in server mode")
@@ -117,6 +124,7 @@ pub fn options() -> OptionParser<Options> {
117124
mem,
118125
vram,
119126
passt_socket,
127+
root_server_port,
120128
server_port,
121129
fex_images,
122130
direct_x11,

crates/muvm/src/guest/bin/muvm-guest.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ fn main() -> Result<()> {
6262
.spawn()?;
6363
}
6464

65+
// Before switching to the user, start another instance of muvm-server to serve
66+
// launch requests as root.
67+
Command::new("muvm-server")
68+
.spawn()
69+
.context("Failed to execute `muvm-server` as child process")?;
70+
6571
let run_path = match setup_user(options.username, options.uid, options.gid) {
6672
Ok(p) => p,
6773
Err(err) => return Err(err).context("Failed to set up user, bailing out"),

crates/muvm/src/net.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ where
1414
Ok(UnixStream::connect(passt_socket_path)?)
1515
}
1616

17-
pub fn start_passt(server_port: u32) -> Result<UnixStream> {
17+
pub fn start_passt(server_port: u32, root_server_port: u32) -> Result<UnixStream> {
1818
// SAFETY: The child process should not inherit the file descriptor of
1919
// `parent_socket`. There is no documented guarantee of this, but the
2020
// implementation as of writing atomically sets `SOCK_CLOEXEC`.
@@ -40,7 +40,9 @@ pub fn start_passt(server_port: u32) -> Result<UnixStream> {
4040
// See https://doc.rust-lang.org/std/io/index.html#io-safety
4141
let child = Command::new("passt")
4242
.args(["-q", "-f", "-t"])
43-
.arg(format!("{server_port}:{server_port}"))
43+
.arg(format!(
44+
"{server_port}:{server_port},{root_server_port}:{root_server_port}"
45+
))
4446
.arg("--fd")
4547
.arg(format!("{}", child_fd.into_raw_fd()))
4648
.spawn();

crates/muvm/src/server/bin/muvm-server.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use std::env;
22
use std::os::unix::process::ExitStatusExt as _;
3+
use std::path::PathBuf;
34

45
use anyhow::{Context, Result};
56
use log::error;
67
use muvm::server::cli_options::options;
78
use muvm::server::worker::{State, Worker};
9+
use nix::unistd::geteuid;
810
use tokio::net::TcpListener;
911
use tokio::process::Command;
1012
use tokio::sync::watch;
@@ -23,19 +25,33 @@ fn main() -> Result<()> {
2325
async fn tokio_main(cookie: String) -> Result<()> {
2426
env_logger::init();
2527

26-
let options = options().run();
2728
let cookie = Uuid::try_parse(&cookie).context("Couldn't parse cookie as UUID v7")?;
29+
let uid: u32 = geteuid().into();
2830

29-
let listener = TcpListener::bind(format!("0.0.0.0:{}", options.server_port)).await?;
31+
let (server_port, command, command_args) = if uid == 0 {
32+
let server_port = if let Ok(server_port) = env::var("MUVM_ROOT_SERVER_PORT") {
33+
server_port.parse()?
34+
} else {
35+
3335
36+
};
37+
(
38+
server_port,
39+
PathBuf::from("/bin/sleep"),
40+
vec!["inf".to_string()],
41+
)
42+
} else {
43+
let options = options().run();
44+
(options.server_port, options.command, options.command_args)
45+
};
46+
47+
let listener = TcpListener::bind(format!("0.0.0.0:{}", server_port)).await?;
3048
let (state_tx, state_rx) = watch::channel(State::new());
3149

3250
let mut worker_handle = tokio::spawn(async move {
3351
let mut worker = Worker::new(cookie, listener, state_tx);
3452
worker.run().await;
3553
});
36-
let command_status = Command::new(&options.command)
37-
.args(options.command_args)
38-
.status();
54+
let command_status = Command::new(&command).args(command_args).status();
3955
tokio::pin!(command_status);
4056
let mut state_rx = WatchStream::new(state_rx);
4157

@@ -63,12 +79,12 @@ async fn tokio_main(cookie: String) -> Result<()> {
6379
if let Some(code) = status.code() {
6480
eprintln!(
6581
"{:?} process exited with status code: {code}",
66-
options.command
82+
command
6783
);
6884
} else {
6985
eprintln!(
7086
"{:?} process terminated by signal: {}",
71-
options.command,
87+
command,
7288
status
7389
.signal()
7490
.expect("either one of status code or signal should be set")
@@ -79,7 +95,7 @@ async fn tokio_main(cookie: String) -> Result<()> {
7995
Err(err) => {
8096
eprintln!(
8197
"Failed to execute {:?} as child process: {err}",
82-
options.command
98+
command
8399
);
84100
},
85101
}

0 commit comments

Comments
 (0)