Skip to content

Commit

Permalink
fix(mmserver): remove unshare as a dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
colinmarc committed Oct 15, 2024
1 parent 0a832c0 commit e5c4575
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 133 deletions.
37 changes: 2 additions & 35 deletions mm-server/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion mm-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-tracy = { version = "0.11", default-features = false }
tracy-client = { version = "0.17", default-features = false }
uds = "0.4"
unshare = "0.7"
uuid = "1"
wayland-protocols = { version = "0.32", features = ["server", "staging", "unstable"] }
wayland-scanner = "0.31"
Expand Down
24 changes: 13 additions & 11 deletions mm-server/src/compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,15 @@ impl Compositor {
create_global::<xwayland_shell_v1::XwaylandShellV1>(&dh, 1);
create_global::<wl_drm::WlDrm>(&dh, 2);

let mut container = Container::new(app_config.clone()).context("initializing container")?;
let mut container = Container::new(
app_config.command.clone(),
app_config.home_isolation_mode.clone().into(),
)
.context("initializing container")?;

for (k, v) in &app_config.env {
container.set_env(k, v);
}

let poll = mio::Poll::new()?;
let waker = Arc::new(mio::Waker::new(poll.registry(), WAKER)?);
Expand Down Expand Up @@ -300,11 +308,9 @@ impl Compositor {
None
};

let mut xwayland = xwayland::XWayland::spawn(
&mut display.handle(),
container.extern_run_path().as_os_str(),
)
.context("spawning Xwayland")?;
let mut xwayland =
xwayland::XWayland::spawn(&mut display.handle(), container.extern_run_path())
.context("spawning Xwayland")?;

// Xwayland writes to this pipe when it's ready.
poll.registry().register(
Expand Down Expand Up @@ -500,11 +506,7 @@ impl Compositor {
}
}
XWAYLAND if event.is_read_closed() => {
let exit = self.xwayland.as_mut().unwrap().child.wait()?;
bail!(
"Xwayland exited unexpectedly with status {}",
exit.code().unwrap_or_default()
);
self.xwayland.as_mut().unwrap().child.wait()?;
}
XWAYLAND if event.is_readable() => {
dump_child_output(
Expand Down
2 changes: 1 addition & 1 deletion mm-server/src/compositor/child.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustix::process::{Pid, Signal, WaitId, WaitidOptions};
use tracing::{debug, info};

mod container;
pub use container::Container;
pub use container::{Container, HomeIsolationMode};

/// A handle to a running container.
pub struct ChildHandle {
Expand Down
63 changes: 34 additions & 29 deletions mm-server/src/compositor/child/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,25 @@ use rustix::{
};
use tracing::debug;

use crate::config::{self, AppConfig};

mod ipc;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HomeIsolationMode {
Unisolated,
Tmpfs,
Permanent(PathBuf),
}

impl From<crate::config::HomeIsolationMode> for HomeIsolationMode {
fn from(value: crate::config::HomeIsolationMode) -> Self {
match value {
crate::config::HomeIsolationMode::Unisolated => Self::Unisolated,
crate::config::HomeIsolationMode::Tmpfs => Self::Tmpfs,
crate::config::HomeIsolationMode::Permanent(p) => Self::Permanent(p),
}
}
}

#[derive(Debug, Clone, Copy)]
struct DevBindMount {
path: &'static str,
Expand Down Expand Up @@ -174,18 +189,15 @@ pub struct Container {
}

impl Container {
pub fn new(app_config: AppConfig) -> anyhow::Result<Self> {
let mut args = app_config.command.clone();
pub fn new(
mut args: Vec<OsString>,
home_isolation_mode: HomeIsolationMode,
) -> anyhow::Result<Self> {
let exe = args.remove(0);
let exe_path =
find_executable_in_path(&exe).ok_or(anyhow!("command {:?} not in PATH", &exe))?;

let mut envs: Vec<CString> = app_config
.env
.clone()
.into_iter()
.map(|(k, v)| make_putenv(k, v))
.collect();
let mut envs = Vec::new();

let mut child_cmd = Command::new(exe_path);
child_cmd.current_dir("/");
Expand All @@ -210,11 +222,11 @@ impl Container {
let intern_home_path: OsString = std::env::var_os("HOME").unwrap_or("/home/mm".into());
envs.push(make_putenv("HOME", intern_home_path.clone()));

debug!(home_mode = ?app_config.home_isolation_mode, "using home mode");
let (extern_home_path, clear_home) = match app_config.home_isolation_mode {
config::HomeIsolationMode::Unisolated => (None, false),
config::HomeIsolationMode::Tmpfs => (None, true),
config::HomeIsolationMode::Permanent(path) => {
debug!(home_mode = ?home_isolation_mode, "using home mode");
let (extern_home_path, clear_home) = match home_isolation_mode {
HomeIsolationMode::Unisolated => (None, false),
HomeIsolationMode::Tmpfs => (None, true),
HomeIsolationMode::Permanent(path) => {
std::fs::create_dir_all(&path).context(format!(
"failed to create home directory {}",
path.display()
Expand Down Expand Up @@ -270,6 +282,10 @@ impl Container {
self.setup_hooks.push(Box::new(f))
}

pub unsafe fn pre_exec(&mut self, f: impl FnMut() -> io::Result<()> + Send + Sync + 'static) {
self.child_cmd.pre_exec(f);
}

pub fn set_env<K, V>(&mut self, key: K, val: V)
where
K: AsRef<OsStr>,
Expand Down Expand Up @@ -841,23 +857,12 @@ mod test {

use rustix::pipe::{pipe_with, PipeFlags};

use crate::{
compositor::Container,
config::{AppConfig, HomeIsolationMode},
};
use crate::compositor::{child::container::HomeIsolationMode, Container};

#[test_log::test]
fn echo() -> anyhow::Result<()> {
let app_config = AppConfig {
description: None,
command: vec!["echo".to_owned().into(), "done".to_owned().into()],
env: Default::default(),
xwayland: false,
force_1x_scale: false,
home_isolation_mode: HomeIsolationMode::Unisolated,
};

let mut container = Container::new(app_config)?;
let mut container =
Container::new(vec!["echo".into(), "done".into()], HomeIsolationMode::Tmpfs)?;
let (pipe_rx, pipe_tx) = pipe_with(PipeFlags::CLOEXEC)?;
container.set_stdout(pipe_tx)?;

Expand Down
16 changes: 2 additions & 14 deletions mm-server/src/compositor/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,7 @@ mod test {
use rustix::pipe::{pipe_with, PipeFlags};

use super::{GamepadLayout, InputDeviceManager};
use crate::{
compositor::Container,
config::{AppConfig, HomeIsolationMode},
};
use crate::compositor::{Container, HomeIsolationMode};

fn run_in_container_with_gamepads<T>(cmd: impl AsRef<[T]>) -> anyhow::Result<String>
where
Expand All @@ -252,16 +249,7 @@ mod test {
.map(|s| s.as_ref().to_owned().into())
.collect();

let app_config = AppConfig {
description: None,
command,
env: Default::default(),
xwayland: false,
force_1x_scale: false,
home_isolation_mode: HomeIsolationMode::Unisolated,
};

let mut container = Container::new(app_config)?;
let mut container = Container::new(command, HomeIsolationMode::Tmpfs)?;
let (pipe_rx, pipe_tx) = pipe_with(PipeFlags::CLOEXEC)?;
container.set_stdout(pipe_tx)?;

Expand Down
74 changes: 32 additions & 42 deletions mm-server/src/compositor/xwayland.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ use pathsearch::find_executable_in_path;
use tracing::{debug, trace};
pub use xwm::*;

use crate::compositor::ClientState;
use crate::compositor::{ClientState, Container, HomeIsolationMode};

pub struct XWayland {
pub display_socket: PathBuf,
pub displayfd_recv: mio::unix::pipe::Receiver,
pub child: unshare::Child,
pub child: super::child::ChildHandle,
pub output: std::io::BufReader<mio::unix::pipe::Receiver>,

xwm_socket: Option<mio::net::UnixStream>,
Expand All @@ -38,8 +38,9 @@ impl XWayland {
let (output_send, output_recv) = mio::unix::pipe::new()?;

// These get dropped when we return, closing the write side (in this process)
let stdout = unshare::Stdio::dup_file(&output_send)?;
let stderr = unshare::Stdio::dup_file(&output_send)?;
let stdout = output_send.as_fd().try_clone_to_owned()?;
let stderr = output_send.as_fd().try_clone_to_owned()?;
drop(output_send);

let (xwm_xwayland, xwm_compositor) = mio::net::UnixStream::pair()?;
let (wayland_xwayland, wayland_compositor) = mio::net::UnixStream::pair()?;
Expand All @@ -56,43 +57,36 @@ impl XWayland {

let socket = mio::net::UnixListener::bind(&socket_path)?;

let exe = find_executable_in_path("Xwayland").ok_or(anyhow!("Xwayland not in PATH"))?;
let mut command = unshare::Command::new(exe);
command
.stdout(stdout)
.stderr(stderr)
.arg("-verbose")
.arg("-rootless")
.arg("-terminate")
.arg("-force-xrandr-emulation")
.arg("-wm")
.arg(xwm_xwayland.as_raw_fd().to_string())
.arg("-displayfd")
.arg(displayfd_send.as_raw_fd().to_string())
.arg("-listenfd")
.arg(socket.as_raw_fd().to_string());

// Setup the environment; clear everything except PATH and XDG_RUNTIME_DIR.
command.env_clear();
for (key, value) in std::env::vars_os() {
if key.to_str() == Some("PATH") {
command.env(key, value);
continue;
}
}

command.env("XDG_RUNTIME_DIR", xdg_runtime_dir.as_ref());
command.env(
let exe = find_executable_in_path("Xwayland")
.ok_or(anyhow!("Xwayland not in PATH"))?
.as_os_str()
.to_owned();
let args = vec![
exe,
"-verbose".into(),
"-rootless".into(),
"-terminate".into(),
"-force-xrandr-emulation".into(),
"-wm".into(),
xwm_xwayland.as_raw_fd().to_string().into(),
"-displayfd".into(),
displayfd_send.as_raw_fd().to_string().into(),
"-listenfd".into(),
socket.as_raw_fd().to_string().into(),
];

let mut container = Container::new(args, HomeIsolationMode::Tmpfs)?;

container.set_env(
"WAYLAND_SOCKET",
format!("{}", wayland_xwayland.as_raw_fd()),
);

unsafe {
command.pre_exec(move || {
// Creates a new process group. This prevents SIGINT sent to
// mmserver from reaching Xwayland.
rustix::process::setsid()?;
container.set_stdout(stdout)?;
container.set_stderr(stderr)?;

unsafe {
container.pre_exec(move || {
// unset the CLOEXEC flag from the sockets we need to pass
// to xwayland.
unset_cloexec(&wayland_xwayland)?;
Expand All @@ -104,12 +98,8 @@ impl XWayland {
});
}

let child = match command.spawn() {
Ok(child) => child,
Err(e) => bail!("failed to spawn Xwayland: {:#}", e),
};

debug!(x11_socket = ?socket_path, pid = child.id(), "spawned Xwayland instance");
let child = container.spawn().context("failed to spawn XWayland")?;
debug!(x11_socket = ?socket_path, "spawned Xwayland instance");

// Insert the client into the display handle. The order is important
// here; XWayland never starts up at all unless it can roundtrip with
Expand Down

0 comments on commit e5c4575

Please sign in to comment.