<NME> xserver.rs
<BEF> /*
 * Steps of XWayland server creation
 *
 * Sockets to create:
 * - a pair for XWayland to connect to smithay as a wayland client, we use our
 *   end to insert the XWayland client in the display
 * - a pair for smithay to connect to XWayland as a WM, we give our end to the
 *   WM and it deals with it
 * - 2 listening sockets on which the XWayland server will listen. We need to
 *   bind them ourselves so we know what value put in the $DISPLAY env variable.
 *   This involves some dance with a lockfile to ensure there is no collision with
 *   an other starting xserver
 *   if we listen on display $D, their paths are respectively:
 *   - /tmp/.X11-unix/X$D
 *   - @/tmp/.X11-unix/X$D (abstract socket)
 *
 * The XWayland server is spawned via an intermediate shell
 * -> wlroots does a double-fork while weston a single one, why ??
 *    -> https://stackoverflow.com/questions/881388/
 * -> once it is started, it will check if SIGUSR1 is set to ignored. If so,
 *    if will consider its parent as "smart", and send a SIGUSR1 signal when
 *    startup completes. We want to catch this so we can launch the VM.
 * -> we need to track if the XWayland crashes, to restart it
 *
 * cf https://github.com/swaywm/wlroots/blob/master/xwayland/xwayland.c
 *
 * Setting SIGUSR1 handler is complicated in multithreaded program, because
use std::rc::Rc;
use std::env;
use std::ffi::CString;
use std::os::unix::io::RawFd;

use nix::{Error as NixError, Result as NixResult};
use nix::errno::Errno;
 * We use an intermediate shell to translate the signal to simple fd IO.
 * We ask sh to setup SIGUSR1 handler, and in a subshell mute SIGUSR1 and exec
 * Xwayland. When the SIGUSR1 is received, it can communicate to us via redirected
use wayland_server::{Client, Display, LoopToken};
use wayland_server::sources::{SignalEvent, Source};

use super::x11_sockets::{make_pair, cleanup_x11_sockets, prepare_x11_sockets};

/// The XWayland handle
pub struct XWayland<WM: XWindowManager> {
        io::{AsRawFd, RawFd},
        net::UnixStream,
        process::CommandExt,
    },
    process::{ChildStdout, Command, Stdio},
    sync::{Arc, Mutex},
};

use calloop::{
    channel::{self, sync_channel, Channel, SyncSender},
    generic::Generic,
    Interest, LoopHandle, Mode,
pub trait XWindowManager {
    /// The XWayland server is ready
    ///
    /// Your previlegied connection to it is this `RawFd`
    fn xwayland_ready(&mut self, fd: RawFd, client: Client);
    /// The XWayland server has exited
    fn xwayland_exited(&mut self);
}
use super::x11_sockets::{prepare_x11_sockets, X11Lock};

/// The XWayland handle
#[derive(Debug)]
pub struct XWayland {
    inner: Arc<Mutex<Inner>>,
}

/// Events generated by the XWayland manager
///
/// This is a very low-level interface, only notifying you when the connection
/// with XWayland is up, or when it terminates.
///
/// Your WM code must be able to handle the XWayland server connecting then
/// disconnecting several time in a row, but only a single connection will
/// be active at any given time.
#[derive(Debug)]
#[must_use = "Connection events must be handled to prevent fd leaking"]
pub enum XWaylandEvent {
    /// The XWayland server is ready
    Ready {
        /// Privileged X11 connection to XWayland
    display: u32,
    wayland_client: Client,
    sigusr1_handler: Option<Source<SignalEvent>>,
    wm_fd: RawFd,
    started_at: ::std::time::Instant,
    child_pid: Option<Pid>,
}

        /// The display number the XWayland server is available at.
        ///
        /// This may be used if you wish to set the `DISPLAY` variable manually when spawning processes that
        /// may use XWayland.
        display: u32,
    },

    /// The XWayland server exited
    ///
    /// This event is sent when the [`XWayland`] handle is dropped.
    Exited,
}

impl XWayland {
        return Ok(());
    }

    let (display, x_fds) = prepare_x11_sockets()?;

    let (x_wm_x11, x_wm_me) = match make_pair() {
        Ok(v) => v,
        Err(()) => {
            // cleanup
            for &f in &x_fds {
                let _ = close(f);
            }
            cleanup_x11_sockets(display);
            return Err(());
        }
    };
    let (wl_x11, wl_me) = match make_pair() {
        Ok(v) => v,
        Err(()) => {
            // cleanup
            for &f in &x_fds {
                let _ = close(f);
            }
            let _ = close(x_wm_x11);
            let _ = close(x_wm_me);
            cleanup_x11_sockets(display);
            return Err(());
        }
    };

    // we have now created all the required sockets

        let (sender, channel) = sync_channel(2);
    let creation_time = ::std::time::Instant::now();

    // create the wayland client for XWayland
    let client = unsafe { guard.wayland_display.borrow_mut().create_client(wl_me) };
    client.set_user_data(Rc::into_raw(inner.clone()) as *const () as *mut ());
    client.set_destructor(client_destroy::<WM>);

    }

    /// Attempt to start the XWayland instance
    ///
    /// If it succeeds, you'll eventually receive an `XWaylandEvent::Ready`
    /// through the source provided by `XWayland::new()` containing an
        }) {
        Ok(v) => v,
        Err(_) => {
            // If that fails (can it even fail ?), cleanup
            for &f in &x_fds {
                let _ = close(f);
            }
            let _ = close(x_wm_x11);
            let _ = close(x_wm_me);
            let _ = close(wl_x11);
            let _ = close(wl_me);
            cleanup_x11_sockets(display);
            return Err(());
        }
        launch(&self.inner, loop_handle, dh)
    }

    /// Shutdown XWayland
    ///
    /// Does nothing if it was not already running, otherwise kills it and you will
    /// later receive a `XWaylandEvent::Exited` event.
    pub fn shutdown(&self) {
        self.inner.lock().unwrap().shutdown();
    }
}

impl Drop for XWayland {
    fn drop(&mut self) {
        self.inner.lock().unwrap().shutdown();
    }
}

#[derive(Debug)]
struct XWaylandInstance {
    display_lock: X11Lock,
    wayland_client: Client,
    wayland_client_fd: RawFd,
    wm_fd: Option<UnixStream>,
    child_stdout: ChildStdout,
}

// Inner implementation of the XWayland manager
#[derive(Debug)]
struct Inner {
    sender: SyncSender<XWaylandEvent>,
    instance: Option<XWaylandInstance>,
    dh: DisplayHandle,
    log: ::slog::Logger,
}

struct XWaylandClientData {
    inner: Arc<Mutex<Inner>>,
}

impl ClientData for XWaylandClientData {
    fn initialized(&self, _client_id: ClientId) {}
    fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {
        // If we are unable to take a lock we are most likely called during
        // a shutdown. This will definitely be the case when the compositor exits
        // and the XWayland instance is dropped.
        if let Ok(mut guard) = self.inner.try_lock() {
            guard.shutdown();
        }
        Err(e) => {
            eprintln!("[smithay] XWayland first fork failed: {:?}", e);
            // fork failed ? cleanup
            for &f in &x_fds {
                let _ = close(f);
            }
            let _ = close(x_wm_x11);
            let _ = close(x_wm_me);
            let _ = close(wl_x11);
            let _ = close(wl_me);
            cleanup_x11_sockets(display);
            return Err(());
        }
    };

    // now, close the FDs that were given to XWayland
    for &f in &x_fds {
        let _ = close(f);
    }
    let _ = close(x_wm_x11);
    let _ = close(wl_x11);

    guard.instance = Some(XWaylandInstance {
        display,
        wayland_client: client,
        sigusr1_handler: Some(sigusr1_handler),
        wm_fd: x_wm_me,
        started_at: creation_time,
        child_pid: Some(child_pid),
    });
    info!(guard.log, "Starting XWayland");

    let (x_wm_x11, x_wm_me) = UnixStream::pair()?;
    let (wl_x11, wl_me) = UnixStream::pair()?;

    let (lock, x_fds) = prepare_x11_sockets(guard.log.clone())?;

    // we have now created all the required sockets

    // all is ready, we can do the fork dance
    let child_stdout = match spawn_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) {
        Ok(child_stdout) => child_stdout,
        Err(e) => {
            if let Some(s) = instance.sigusr1_handler.take() {
                s.remove();
            }
            // close the WM fd
            let _ = close(instance.wm_fd);
            // cleanup the X11 sockets
            cleanup_x11_sockets(instance.display);
            ::std::env::remove_var("DISPLAY");
                // the closure must be called exactly one time, this cannot panic
                xwayland_ready(&loop_inner);
                Ok(calloop::PostAction::Remove)
            },
        )
        .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;

    let client_fd = wl_me.as_raw_fd();
    let client = dh.insert_client(wl_me, Arc::new(XWaylandClientData { inner: inner.clone() }))?;
    guard.instance = Some(XWaylandInstance {
        display_lock: lock,
        wayland_client: client,
        wayland_client_fd: client_fd,
        wm_fd: Some(x_wm_me),
        child_stdout,
    });

    Ok(())
}

/// An event source for monitoring XWayland status
///
/// You need to insert it in a [`calloop`] event loop to handle the events it produces,
/// of type [`XWaylandEvent`], which notify you about startup and shutdown of the Xwayland
/// instance.
#[derive(Debug)]
#[must_use]
pub struct XWaylandSource {
    channel: Channel<XWaylandEvent>,
}

impl calloop::EventSource for XWaylandSource {
    type Event = XWaylandEvent;
    type Metadata = ();
    type Ret = ();
    type Error = io::Error;

    fn process_events<F>(
        &mut self,
        readiness: calloop::Readiness,
        token: calloop::Token,
        mut callback: F,
    ) -> io::Result<calloop::PostAction>
    where
        F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
    {
        self.channel
            .process_events(readiness, token, |event, &mut ()| match event {
                channel::Event::Msg(msg) => callback(msg, &mut ()),
                channel::Event::Closed => {}
            })
            .map_err(|err| io::Error::new(io::ErrorKind::BrokenPipe, err))

    if success {
        // signal the WM
        wm.xwayland_ready(instance.wm_fd, instance.wayland_client.clone());

        // setup the environemnt
        ::std::env::set_var("DISPLAY", format!(":{}", instance.display));
    fn reregister(
        &mut self,
        poll: &mut calloop::Poll,
        factory: &mut calloop::TokenFactory,
    ) -> calloop::Result<()> {
        self.channel.reregister(poll, factory)
    }

    fn unregister(&mut self, poll: &mut calloop::Poll) -> calloop::Result<()> {
        self.channel.unregister(poll)
    }
}
/// If this returns, that means that something failed
fn exec_xwayland(
    display: u32,
    wayland_socket: RawFd,
    wm_socket: RawFd,
    listen_sockets: &[RawFd],
) -> NixResult<Void> {
    // uset the CLOEXEC flag from the sockets we need to pass
    // to xwayland
    unset_cloexec(wayland_socket)?;
    unset_cloexec(wm_socket)?;
    for &socket in listen_sockets {
        unset_cloexec(socket)?;
    }
    // prepare the arguments to Xwayland

            // Remove DISPLAY from the env
            ::std::env::remove_var("DISPLAY");
        CString::new("-rootless").unwrap(),
        CString::new("-terminate").unwrap(),
        CString::new("-wm").unwrap(),
        CString::new(format!("{}", wm_socket)).unwrap(),
    ];
    for &socket in listen_sockets {
        args.push(CString::new("-listen").unwrap());
        args.push(CString::new(format!("{}", socket)).unwrap());
    }
    // setup the environment: clear everything except PATH and XDG_RUNTIME_DIR
    for (key, _) in env::vars_os() {
    let guard = &mut *guard;
    info!(guard.log, "XWayland ready");
    // instance should never be None at this point
        env::remove_var(key);
    }
    // the WAYLAND_SOCKET var tells Xwayland where to connect as a wayland client
    env::set_var("WAYLAND_SOCKET", format!("{}", wayland_socket));

    // ignore SIGUSR1, this will make the Xwayland server send us this
    // signal when it is ready apparently
        Ok(len) => len > 0 && buffer[0] == b'S',
        Err(e) => {
            error!(guard.log, "Checking launch status failed"; "err" => format!("{:?}", e));
            false
        }
    };

    if success {
        // setup the environment
        ::std::env::set_var("DISPLAY", format!(":{}", instance.display_lock.display()));

        // signal the WM
        info!(
            guard.log,
            "XWayland is ready on DISPLAY \":{}\", signaling the WM.",
///
/// This means that the Fd will *not* be automatically
/// closed when we exec() into Xwayland
fn unset_cloexec(fd: RawFd) -> NixResult<()> {
    use nix::fcntl::{fcntl, FcntlArg, FdFlag};
    fcntl(fd, FcntlArg::F_SETFD(FdFlag::empty()))?;
    Ok(())
}
        });
    } else {
        error!(
            guard.log,
            "XWayland crashed at startup, will not try to restart it."
        );
    }
}

/// Spawn XWayland with given sockets on given display
///
/// Returns a pipe that outputs 'S' upon successful launch.
fn spawn_xwayland(
    display: u32,
    wayland_socket: UnixStream,
    wm_socket: UnixStream,
    listen_sockets: &[UnixStream],
) -> io::Result<ChildStdout> {
    let mut command = Command::new("sh");

    // We use output stream to communicate because FD is easier to handle than exit code.
    command.stdout(Stdio::piped());

    let mut xwayland_args = format!(":{} -rootless -terminate -wm {}", display, wm_socket.as_raw_fd());
    for socket in listen_sockets {
        // Will only fail to write on OOM, so this panic is fine.
        write!(xwayland_args, " -listenfd {}", socket.as_raw_fd()).unwrap();
    }
    // This command let sh to:
    // * Set up signal handler for USR1
    // * Launch Xwayland with USR1 ignored so Xwayland will signal us when it is ready (also redirect
    //   Xwayland's STDOUT to STDERR so its output, if any, won't distract us)
    // * Print "S" and exit if USR1 is received
    command.arg("-c").arg(format!(
        "trap 'echo S' USR1; (trap '' USR1; exec Xwayland {}) 1>&2 & wait",
        xwayland_args
    ));

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

    unsafe {
        let wayland_socket_fd = wayland_socket.as_raw_fd();
        let wm_socket_fd = wm_socket.as_raw_fd();
        let socket_fds: Vec<_> = listen_sockets.iter().map(|socket| socket.as_raw_fd()).collect();
        command.pre_exec(move || {
            // unset the CLOEXEC flag from the sockets we need to pass
            // to xwayland
            unset_cloexec(wayland_socket_fd)?;
            unset_cloexec(wm_socket_fd)?;
            for &socket in socket_fds.iter() {
                unset_cloexec(socket)?;
            }
            Ok(())
        });
    }

    let mut child = command.spawn()?;
    Ok(child.stdout.take().expect("stdout should be piped"))
}

/// Remove the `O_CLOEXEC` flag from this `Fd`
///
/// This means that the `Fd` will *not* be automatically
/// closed when we `exec()` into XWayland
fn unset_cloexec(fd: RawFd) -> io::Result<()> {
    use nix::fcntl::{fcntl, FcntlArg, FdFlag};
    fcntl(fd, FcntlArg::F_SETFD(FdFlag::empty()))?;
    Ok(())
}

<MSG> xwayland: Use Rust's UnixStream instead of RawFd

THis allows us to take advantage of RAII for cleanup, among others.

<DFF> @@ -28,7 +28,8 @@ use std::cell::RefCell;
 use std::rc::Rc;
 use std::env;
 use std::ffi::CString;
-use std::os::unix::io::RawFd;
+use std::os::unix::io::{RawFd, AsRawFd, IntoRawFd};
+use std::os::unix::net::UnixStream;
 
 use nix::{Error as NixError, Result as NixResult};
 use nix::errno::Errno;
@@ -38,7 +39,7 @@ use nix::sys::signal;
 use wayland_server::{Client, Display, LoopToken};
 use wayland_server::sources::{SignalEvent, Source};
 
-use super::x11_sockets::{make_pair, cleanup_x11_sockets, prepare_x11_sockets};
+use super::x11_sockets::{cleanup_x11_sockets, prepare_x11_sockets};
 
 /// The XWayland handle
 pub struct XWayland<WM: XWindowManager> {
@@ -57,8 +58,8 @@ pub struct XWayland<WM: XWindowManager> {
 pub trait XWindowManager {
     /// The XWayland server is ready
     ///
-    /// Your previlegied connection to it is this `RawFd`
-    fn xwayland_ready(&mut self, fd: RawFd, client: Client);
+    /// Your previlegied connection to it is this `UnixStream`
+    fn xwayland_ready(&mut self, connection: UnixStream, client: Client);
     /// The XWayland server has exited
     fn xwayland_exited(&mut self);
 }
@@ -87,7 +88,7 @@ struct XWaylandInstance {
     display: u32,
     wayland_client: Client,
     sigusr1_handler: Option<Source<SignalEvent>>,
-    wm_fd: RawFd,
+    wm_fd: Option<UnixStream>,
     started_at: ::std::time::Instant,
     child_pid: Option<Pid>,
 }
@@ -109,32 +110,10 @@ fn launch<WM: XWindowManager + 'static>(inner: &Rc<RefCell<Inner<WM>>>) -> Resul
         return Ok(());
     }
 
-    let (display, x_fds) = prepare_x11_sockets()?;
+    let (x_wm_x11, x_wm_me) = UnixStream::pair().map_err(|_| ())?;
+    let (wl_x11, wl_me) = UnixStream::pair().map_err(|_| ())?;
 
-    let (x_wm_x11, x_wm_me) = match make_pair() {
-        Ok(v) => v,
-        Err(()) => {
-            // cleanup
-            for &f in &x_fds {
-                let _ = close(f);
-            }
-            cleanup_x11_sockets(display);
-            return Err(());
-        }
-    };
-    let (wl_x11, wl_me) = match make_pair() {
-        Ok(v) => v,
-        Err(()) => {
-            // cleanup
-            for &f in &x_fds {
-                let _ = close(f);
-            }
-            let _ = close(x_wm_x11);
-            let _ = close(x_wm_me);
-            cleanup_x11_sockets(display);
-            return Err(());
-        }
-    };
+    let (display, x_fds) = prepare_x11_sockets()?;
 
     // we have now created all the required sockets
 
@@ -142,7 +121,7 @@ fn launch<WM: XWindowManager + 'static>(inner: &Rc<RefCell<Inner<WM>>>) -> Resul
     let creation_time = ::std::time::Instant::now();
 
     // create the wayland client for XWayland
-    let client = unsafe { guard.wayland_display.borrow_mut().create_client(wl_me) };
+    let client = unsafe { guard.wayland_display.borrow_mut().create_client(wl_me.into_raw_fd()) };
     client.set_user_data(Rc::into_raw(inner.clone()) as *const () as *mut ());
     client.set_destructor(client_destroy::<WM>);
 
@@ -155,14 +134,6 @@ fn launch<WM: XWindowManager + 'static>(inner: &Rc<RefCell<Inner<WM>>>) -> Resul
         }) {
         Ok(v) => v,
         Err(_) => {
-            // If that fails (can it even fail ?), cleanup
-            for &f in &x_fds {
-                let _ = close(f);
-            }
-            let _ = close(x_wm_x11);
-            let _ = close(x_wm_me);
-            let _ = close(wl_x11);
-            let _ = close(wl_me);
             cleanup_x11_sockets(display);
             return Err(());
         }
@@ -218,30 +189,16 @@ fn launch<WM: XWindowManager + 'static>(inner: &Rc<RefCell<Inner<WM>>>) -> Resul
         Err(e) => {
             eprintln!("[smithay] XWayland first fork failed: {:?}", e);
             // fork failed ? cleanup
-            for &f in &x_fds {
-                let _ = close(f);
-            }
-            let _ = close(x_wm_x11);
-            let _ = close(x_wm_me);
-            let _ = close(wl_x11);
-            let _ = close(wl_me);
             cleanup_x11_sockets(display);
             return Err(());
         }
     };
 
-    // now, close the FDs that were given to XWayland
-    for &f in &x_fds {
-        let _ = close(f);
-    }
-    let _ = close(x_wm_x11);
-    let _ = close(wl_x11);
-
     guard.instance = Some(XWaylandInstance {
         display,
         wayland_client: client,
         sigusr1_handler: Some(sigusr1_handler),
-        wm_fd: x_wm_me,
+        wm_fd: Some(x_wm_me),
         started_at: creation_time,
         child_pid: Some(child_pid),
     });
@@ -261,8 +218,10 @@ impl<WM: XWindowManager> Inner<WM> {
             if let Some(s) = instance.sigusr1_handler.take() {
                 s.remove();
             }
-            // close the WM fd
-            let _ = close(instance.wm_fd);
+            // close the WM fd if it is still there
+            if let Some(s) = instance.wm_fd.take() {
+                let _ = s.shutdown(::std::net::Shutdown::Both);
+            }
             // cleanup the X11 sockets
             cleanup_x11_sockets(instance.display);
             ::std::env::remove_var("DISPLAY");
@@ -321,7 +280,10 @@ fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
 
     if success {
         // signal the WM
-        wm.xwayland_ready(instance.wm_fd, instance.wayland_client.clone());
+        wm.xwayland_ready(
+            instance.wm_fd.take().unwrap(), // This is a bug if None
+            instance.wayland_client.clone()
+        );
 
         // setup the environemnt
         ::std::env::set_var("DISPLAY", format!(":{}", instance.display));
@@ -340,15 +302,15 @@ enum Void {}
 /// If this returns, that means that something failed
 fn exec_xwayland(
     display: u32,
-    wayland_socket: RawFd,
-    wm_socket: RawFd,
-    listen_sockets: &[RawFd],
+    wayland_socket: UnixStream,
+    wm_socket: UnixStream,
+    listen_sockets: &[UnixStream],
 ) -> NixResult<Void> {
     // uset the CLOEXEC flag from the sockets we need to pass
     // to xwayland
-    unset_cloexec(wayland_socket)?;
-    unset_cloexec(wm_socket)?;
-    for &socket in listen_sockets {
+    unset_cloexec(&wayland_socket)?;
+    unset_cloexec(&wm_socket)?;
+    for socket in listen_sockets {
         unset_cloexec(socket)?;
     }
     // prepare the arguments to Xwayland
@@ -358,11 +320,11 @@ fn exec_xwayland(
         CString::new("-rootless").unwrap(),
         CString::new("-terminate").unwrap(),
         CString::new("-wm").unwrap(),
-        CString::new(format!("{}", wm_socket)).unwrap(),
+        CString::new(format!("{}", wm_socket.as_raw_fd())).unwrap(),
     ];
-    for &socket in listen_sockets {
+    for socket in listen_sockets {
         args.push(CString::new("-listen").unwrap());
-        args.push(CString::new(format!("{}", socket)).unwrap());
+        args.push(CString::new(format!("{}", socket.as_raw_fd())).unwrap());
     }
     // setup the environment: clear everything except PATH and XDG_RUNTIME_DIR
     for (key, _) in env::vars_os() {
@@ -372,7 +334,7 @@ fn exec_xwayland(
         env::remove_var(key);
     }
     // the WAYLAND_SOCKET var tells Xwayland where to connect as a wayland client
-    env::set_var("WAYLAND_SOCKET", format!("{}", wayland_socket));
+    env::set_var("WAYLAND_SOCKET", format!("{}", wayland_socket.as_raw_fd()));
 
     // ignore SIGUSR1, this will make the Xwayland server send us this
     // signal when it is ready apparently
@@ -394,8 +356,8 @@ fn exec_xwayland(
 ///
 /// This means that the Fd will *not* be automatically
 /// closed when we exec() into Xwayland
-fn unset_cloexec(fd: RawFd) -> NixResult<()> {
+fn unset_cloexec<F: AsRawFd>(fd: &F) -> NixResult<()> {
     use nix::fcntl::{fcntl, FcntlArg, FdFlag};
-    fcntl(fd, FcntlArg::F_SETFD(FdFlag::empty()))?;
+    fcntl(fd.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::empty()))?;
     Ok(())
 }

xwayland: Use Rust's UnixStream instead of RawFd