diff --git a/src/lib.rs b/src/lib.rs index 97035b8..71b36bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ use log::{error, info}; use rustix::event::{poll, PollFd, PollFlags}; use smithay_client_toolkit::data_device_manager::WritePipe; use std::io::{BufRead, BufReader, Read, Write}; -use std::os::fd::{AsFd, AsRawFd, BorrowedFd}; +use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd}; use std::os::unix::net::UnixStream; use std::process::{Command, ExitStatus, Stdio}; use wayland_server::{Display, ListeningSocket}; @@ -35,6 +35,7 @@ type RealServerState = ServerState; pub trait RunData { fn display(&self) -> Option<&str>; + fn listenfds(&mut self) -> Vec; fn server(&self) -> Option { None } @@ -46,7 +47,7 @@ pub trait RunData { fn xwayland_ready(&self, _display: String, _pid: u32) {} } -pub fn main(data: impl RunData) -> Option<()> { +pub fn main(mut data: impl RunData) -> Option<()> { let mut version = env!("VERGEN_GIT_DESCRIBE"); if version == "VERGEN_IDEMPOTENT_OUTPUT" { version = env!("CARGO_PKG_VERSION"); @@ -70,6 +71,12 @@ pub fn main(data: impl RunData) -> Option<()> { if let Some(display) = data.display() { xwayland.arg(display); } + + let fds = data.listenfds(); + for fd in &fds { + xwayland.args(["-listenfd", &fd.as_raw_fd().to_string()]); + } + let mut xwayland = xwayland .args([ "-rootless", @@ -84,6 +91,9 @@ pub fn main(data: impl RunData) -> Option<()> { .spawn() .unwrap(); + // Now that Xwayland spawned and got the listenfds, we can close them here. + drop(fds); + let xwl_pid = xwayland.id(); let (mut finish_tx, mut finish_rx) = UnixStream::pair().unwrap(); diff --git a/src/main.rs b/src/main.rs index 5a1005d..a570b0a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,63 @@ +use std::os::fd::{FromRawFd, OwnedFd, RawFd}; + fn main() { pretty_env_logger::formatted_timed_builder() .filter_level(log::LevelFilter::Info) .parse_default_env() .init(); - xwayland_satellite::main(RealData(get_display())); + xwayland_satellite::main(parse_args()); } -#[repr(transparent)] -struct RealData(Option); +struct RealData { + display: Option, + listenfds: Vec, +} impl xwayland_satellite::RunData for RealData { fn display(&self) -> Option<&str> { - self.0.as_deref() + self.display.as_deref() + } + + fn listenfds(&mut self) -> Vec { + std::mem::take(&mut self.listenfds) } } -fn get_display() -> Option { +fn parse_args() -> RealData { + let mut data = RealData { + display: None, + listenfds: Vec::new(), + }; + let mut args: Vec<_> = std::env::args().collect(); - if args.len() > 2 { - panic!("Unexpected arguments: {:?}", &args[2..]); + if args.len() < 2 { + return data; } - (args.len() == 2).then(|| args.swap_remove(1)) + // Argument at index 1 is our display name. The rest can be -listenfd. + let mut i = 2; + while i < args.len() { + let arg = &args[i]; + if arg == "-listenfd" { + let next = i + 1; + if next == args.len() { + // Matches the Xwayland error message. + panic!("Required argument to -listenfd not specified"); + } + + let fd: RawFd = args[next].parse().expect("Error parsing -listenfd number"); + // SAFETY: + // - whoever runs the binary must ensure this fd is open and valid. + // - parse_args() must only be called once to avoid double closing. + let fd = unsafe { OwnedFd::from_raw_fd(fd) }; + + data.listenfds.push(fd); + i += 2; + } else { + panic!("Unrecognized argument: {arg}"); + } + } + + data.display = Some(args.swap_remove(1)); + + data } diff --git a/tests/integration.rs b/tests/integration.rs index 931520a..99774fa 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -3,7 +3,7 @@ use rustix::process::{Pid, Signal, WaitOptions}; use std::collections::HashMap; use std::io::Write; use std::mem::ManuallyDrop; -use std::os::fd::{AsRawFd, BorrowedFd}; +use std::os::fd::{AsRawFd, BorrowedFd, OwnedFd}; use std::os::unix::net::UnixStream; use std::sync::{ atomic::{AtomicBool, Ordering}, @@ -72,6 +72,10 @@ impl xwls::RunData for TestData { None } + fn listenfds(&mut self) -> Vec { + Vec::new() + } + fn server(&self) -> Option { let mut server = self.server.lock().unwrap(); assert!(server.is_some());