<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
 * Xwayland will send SIGUSR1 to the process, and if a thread cannot handle
 * SIGUSR1, that thread will be killed.
 *
 * Double-fork can tackle this issue, but this is also very complex in a
 * a multithread program, after forking only signal-safe functions can be used.
    any::Any,
    cell::RefCell,
    env,
    ffi::CString,
    os::unix::{
        io::{AsRawFd, IntoRawFd, RawFd},
        net::UnixStream,
    },
    rc::Rc,
    sync::Arc,
};
        io::{AsRawFd, RawFd},
        net::UnixStream,
        process::CommandExt,
    Interest, LoopHandle, Mode, Source,
};

use nix::Result as NixResult;

use wayland_server::{Client, Display, Filter};

use super::{
    x11_sockets::{prepare_x11_sockets, X11Lock},
    LaunchHelper,
};

/// The XWayland handle
pub struct XWayland<WM: XWindowManager> {
    backend::{ClientData, ClientId, DisconnectReason},
    Client, DisplayHandle,
};

use slog::{error, info, o};

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
        display: Rc<RefCell<Display>>,
        data: &mut T,
        logger: L,
        helper: LaunchHelper,
    ) -> Result<XWayland<WM>, ()>
    where
        L: Into<Option<::slog::Logger>>,

        /// Wayland client file descriptor in case you are not using the display's poll_fd
        client_fd: RawFd,

        /// 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 {
    /// Create a new XWayland manager
            }),
            wayland_display: display,
            instance: None,
            helper,
            log: log.new(o!("smithay_module" => "XWayland")),
        }));
        launch(&inner, data)?;
    {
        let log = crate::slog_or_fallback(logger);
        // We don't expect to ever have more than 2 messages in flight, if XWayland got ready and then died right away
        let (sender, channel) = sync_channel(2);
        let inner = Arc::new(Mutex::new(Inner {
            instance: None,
            sender,
            dh: dh.clone(),
            log: log.new(o!("smithay_module" => "XWayland")),
        }));
        (XWayland { inner }, XWaylandSource { channel })
    }

    startup_handler: Option<Source<Generic<Fd>>>,
    wm_fd: Option<UnixStream>,
    started_at: ::std::time::Instant,
}

type SourceMaker<WM> = dyn FnMut(Rc<RefCell<Inner<WM>>>, RawFd) -> Result<Source<Generic<Fd>>, ()>;
    /// Does nothing if XWayland is already started or starting.
    pub fn start<D>(&self, loop_handle: LoopHandle<'_, D>) -> io::Result<()> {
        let dh = self.inner.lock().unwrap().dh.clone();
        launch(&self.inner, loop_handle, dh)
    }
    wayland_display: Rc<RefCell<Display>>,
    instance: Option<XWaylandInstance>,
    kill_source: Box<dyn Fn(Source<Generic<Fd>>)>,
    helper: LaunchHelper,
    log: ::slog::Logger,
}

        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) {}
    }));

    // all is ready, we can do the fork dance
    match guard.helper.launch(lock.display(), wl_x11, x_wm_x11, &x_fds) {
        Ok(()) => {}
        Err(e) => {
            error!(guard.log, "Could not initiate launch of Xwayland"; "err" => format!("{:?}", e));
            return Err(());
        }
    }

    let status_fd = guard.helper.status_fd();
    let startup_handler = (&mut *guard.source_maker)(inner.clone(), status_fd)?;

    guard.instance = Some(XWaylandInstance {
        display_lock: lock,
    loop_handle: LoopHandle<'_, D>,
        startup_handler: Some(startup_handler),
        wm_fd: Some(x_wm_me),
        started_at: creation_time,
    });

    Ok(())
    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) => {
            error!(guard.log, "XWayland failed to spawn"; "err" => format!("{:?}", e));
            return Err(e);
        }
    };

    let loop_inner = inner.clone();
    loop_handle
        .insert_source(
            Generic::<_, io::Error>::new(child_stdout.as_raw_fd(), Interest::READ, Mode::Level),
            move |_, _, _| {
                // 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]
    // instance should never be None at this point
    let instance = inner.instance.as_mut().unwrap();
    let wm = &mut inner.wm;

    let success = match inner.helper.was_launch_succesful() {
        Ok(s) => s,
        Err(e) => {
            error!(inner.log, "Checking launch status failed"; "err" => format!("{:?}", e));
            false
        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))
    }

    fn register(
        &mut self,
        poll: &mut calloop::Poll,
        factory: &mut calloop::TokenFactory,
    ) -> calloop::Result<()> {
        self.channel.register(poll, factory)
    }

    fn reregister(
    }
}

pub(crate) enum Void {}

/// Exec XWayland with given sockets on given display
///
/// If this returns, that means that something failed
pub(crate) fn exec_xwayland(
    display: u32,
    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(socket)?;
    }
    // prepare the arguments to XWayland
    let mut args = vec![
        CString::new("Xwayland").unwrap(),
        CString::new(format!(":{}", display)).unwrap(),
        CString::new("-rootless").unwrap(),
        CString::new("-terminate").unwrap(),
        CString::new("-wm").unwrap(),
        CString::new(format!("{}", wm_socket.as_raw_fd())).unwrap(),
    ];
    for socket in listen_sockets {
        args.push(CString::new("-listen").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() {
        if key.to_str() == Some("PATH") || key.to_str() == Some("XDG_RUNTIME_DIR") {
            continue;
        }
        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.as_raw_fd()));

    // ignore SIGUSR1, this will make the XWayland server send us this
    // signal when it is ready apparently
    unsafe {
        use nix::sys::signal::*;
        sigaction(
            Signal::SIGUSR1,
            &SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty()),
        )?;
    }

    // run it
    let ret = ::nix::unistd::execvp(
        &CString::new("Xwayland").unwrap(),
        &args.iter().map(AsRef::as_ref).collect::<Vec<_>>(),
    )?;
    // small dance to actually return Void
    match ret {}
}

/// 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<F: AsRawFd>(fd: &F) -> NixResult<()> {
    use nix::fcntl::{fcntl, FcntlArg, FdFlag};
    fcntl(fd.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::empty()))?;
    Ok(())
}
    }
}

/// 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> Use sh to handle SIGUSR1 and replace fork+exec with spawn

<DFF> @@ -33,11 +33,13 @@ use std::{
     any::Any,
     cell::RefCell,
     env,
-    ffi::CString,
+    io::{Error as IOError, Read, Result as IOResult},
     os::unix::{
         io::{AsRawFd, IntoRawFd, RawFd},
         net::UnixStream,
+        process::CommandExt,
     },
+    process::{ChildStdout, Command, Stdio},
     rc::Rc,
     sync::Arc,
 };
@@ -47,14 +49,11 @@ use calloop::{
     Interest, LoopHandle, Mode, Source,
 };
 
-use nix::Result as NixResult;
+use nix::Error as NixError;
 
 use wayland_server::{Client, Display, Filter};
 
-use super::{
-    x11_sockets::{prepare_x11_sockets, X11Lock},
-    LaunchHelper,
-};
+use super::x11_sockets::{prepare_x11_sockets, X11Lock};
 
 /// The XWayland handle
 pub struct XWayland<WM: XWindowManager> {
@@ -87,7 +86,6 @@ impl<WM: XWindowManager + 'static> XWayland<WM> {
         display: Rc<RefCell<Display>>,
         data: &mut T,
         logger: L,
-        helper: LaunchHelper,
     ) -> Result<XWayland<WM>, ()>
     where
         L: Into<Option<::slog::Logger>>,
@@ -113,7 +111,6 @@ impl<WM: XWindowManager + 'static> XWayland<WM> {
             }),
             wayland_display: display,
             instance: None,
-            helper,
             log: log.new(o!("smithay_module" => "XWayland")),
         }));
         launch(&inner, data)?;
@@ -133,6 +130,7 @@ struct XWaylandInstance {
     startup_handler: Option<Source<Generic<Fd>>>,
     wm_fd: Option<UnixStream>,
     started_at: ::std::time::Instant,
+    child_stdout: Option<ChildStdout>,
 }
 
 type SourceMaker<WM> = dyn FnMut(Rc<RefCell<Inner<WM>>>, RawFd) -> Result<Source<Generic<Fd>>, ()>;
@@ -144,7 +142,6 @@ struct Inner<WM: XWindowManager> {
     wayland_display: Rc<RefCell<Display>>,
     instance: Option<XWaylandInstance>,
     kill_source: Box<dyn Fn(Source<Generic<Fd>>)>,
-    helper: LaunchHelper,
     log: ::slog::Logger,
 }
 
@@ -185,16 +182,15 @@ fn launch<WM: XWindowManager + 'static, T: Any>(
     }));
 
     // all is ready, we can do the fork dance
-    match guard.helper.launch(lock.display(), wl_x11, x_wm_x11, &x_fds) {
-        Ok(()) => {}
+    let child_stdout = match spawn_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) {
+        Ok(child_stdout) => child_stdout,
         Err(e) => {
-            error!(guard.log, "Could not initiate launch of Xwayland"; "err" => format!("{:?}", e));
+            error!(guard.log, "XWayland failed to spawn"; "err" => format!("{:?}", e));
             return Err(());
         }
-    }
+    };
 
-    let status_fd = guard.helper.status_fd();
-    let startup_handler = (&mut *guard.source_maker)(inner.clone(), status_fd)?;
+    let startup_handler = (&mut *guard.source_maker)(inner.clone(), child_stdout.as_raw_fd())?;
 
     guard.instance = Some(XWaylandInstance {
         display_lock: lock,
@@ -202,6 +198,7 @@ fn launch<WM: XWindowManager + 'static, T: Any>(
         startup_handler: Some(startup_handler),
         wm_fd: Some(x_wm_me),
         started_at: creation_time,
+        child_stdout: Some(child_stdout),
     });
 
     Ok(())
@@ -258,9 +255,13 @@ fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
     // instance should never be None at this point
     let instance = inner.instance.as_mut().unwrap();
     let wm = &mut inner.wm;
+    // neither the child_stdout
+    let child_stdout = instance.child_stdout.as_mut().unwrap();
 
-    let success = match inner.helper.was_launch_succesful() {
-        Ok(s) => s,
+    // This reads the one byte that is written when sh receives SIGUSR1
+    let mut buffer = [0];
+    let success = match child_stdout.read(&mut buffer) {
+        Ok(len) => len > 0 && buffer[0] == b'S',
         Err(e) => {
             error!(inner.log, "Checking launch status failed"; "err" => format!("{:?}", e));
             false
@@ -290,72 +291,79 @@ fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
     }
 }
 
-pub(crate) enum Void {}
-
 /// Exec XWayland with given sockets on given display
 ///
 /// If this returns, that means that something failed
-pub(crate) fn exec_xwayland(
+fn spawn_xwayland(
     display: u32,
     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(socket)?;
-    }
-    // prepare the arguments to XWayland
-    let mut args = vec![
-        CString::new("Xwayland").unwrap(),
-        CString::new(format!(":{}", display)).unwrap(),
-        CString::new("-rootless").unwrap(),
-        CString::new("-terminate").unwrap(),
-        CString::new("-wm").unwrap(),
-        CString::new(format!("{}", wm_socket.as_raw_fd())).unwrap(),
-    ];
+) -> IOResult<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 {
-        args.push(CString::new("-listen").unwrap());
-        args.push(CString::new(format!("{}", socket.as_raw_fd())).unwrap());
+        xwayland_args.push_str(&format!(" -listen {}", socket.as_raw_fd()));
     }
-    // setup the environment: clear everything except PATH and XDG_RUNTIME_DIR
-    for (key, _) in env::vars_os() {
+    // 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;
         }
-        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.as_raw_fd()));
+    command.env("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
     unsafe {
-        use nix::sys::signal::*;
-        sigaction(
-            Signal::SIGUSR1,
-            &SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty()),
-        )?;
+        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(())
+        });
     }
 
-    // run it
-    let ret = ::nix::unistd::execvp(
-        &CString::new("Xwayland").unwrap(),
-        &args.iter().map(AsRef::as_ref).collect::<Vec<_>>(),
-    )?;
-    // small dance to actually return Void
-    match ret {}
+    let mut child = command.spawn()?;
+    Ok(child.stdout.take().expect("stdout should be piped"))
+}
+
+fn nix_error_to_io(err: NixError) -> IOError {
+    use std::io::ErrorKind;
+    match err {
+        NixError::Sys(errno) => errno.into(),
+        NixError::InvalidPath | NixError::InvalidUtf8 => IOError::new(ErrorKind::InvalidInput, err),
+        NixError::UnsupportedOperation => IOError::new(ErrorKind::Other, err),
+    }
 }
 
 /// 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<F: AsRawFd>(fd: &F) -> NixResult<()> {
+fn unset_cloexec(fd: RawFd) -> IOResult<()> {
     use nix::fcntl::{fcntl, FcntlArg, FdFlag};
-    fcntl(fd.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::empty()))?;
+    fcntl(fd, FcntlArg::F_SETFD(FdFlag::empty())).map_err(nix_error_to_io)?;
     Ok(())
 }

Use sh to handle SIGUSR1 and replace fork+exec with spawn