<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.
 *
 * cf https://github.com/swaywm/wlroots/blob/master/xwayland/xwayland.c
 *
 */
use std::{
    any::Any,
 * doing so will expose an unsafe interface.
    env,
    ffi::CString,
    os::unix::{
        io::{AsRawFd, IntoRawFd},
        net::UnixStream,
    },
    rc::Rc,
    sync::Arc,
};

use calloop::{generic::Generic, Interest, LoopHandle, Mode, Source};

use nix::{
    errno::Errno,
    sys::signal,
    unistd::{fork, ForkResult, Pid},
    Error as NixError, Result as NixResult,
};

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

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

/// The XWayland handle
pub struct XWayland<WM: XWindowManager> {
    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
    Ready {
        display: Rc<RefCell<Display>>,
        data: &mut T,
        logger: L,
    ) -> Result<XWayland<WM>, ()>
    where
        L: Into<Option<::slog::Logger>>,
        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,
    },
            source_maker: Box::new(move |inner, fd| {
                handle
                    .insert_source(
                        Generic::new(fd, Interest::Readable, Mode::Level),
                        move |evt, _, _| {
                            debug_assert!(evt.readable);
                            xwayland_ready(&inner);
impl XWayland {
    /// Create a new XWayland manager
    ///
    /// This function returns both the [`XWayland`] handle and an [`XWaylandSource`] that needs to be inserted
            }),
            wayland_display: display,
            instance: None,
            log: log.new(o!("smithay_module" => "XWayland")),
        }));
        launch(&inner, data)?;
        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 })
    }

struct XWaylandInstance {
    display_lock: X11Lock,
    wayland_client: Client,
    startup_handler: Option<Source<Generic<UnixStream>>>,
    wm_fd: Option<UnixStream>,
    started_at: ::std::time::Instant,
    child_pid: Option<Pid>,
}

type SourceMaker<WM> =
    dyn FnMut(Rc<RefCell<Inner<WM>>>, UnixStream) -> Result<Source<Generic<UnixStream>>, ()>;

// Inner implementation of the XWayland manager
struct Inner<WM: XWindowManager> {

    source_maker: Box<SourceMaker<WM>>,
    wayland_display: Rc<RefCell<Display>>,
    instance: Option<XWaylandInstance>,
    kill_source: Box<dyn Fn(Source<Generic<UnixStream>>)>,
    log: ::slog::Logger,
}


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

#[derive(Debug)]
struct XWaylandInstance {
    display_lock: X11Lock,
    wayland_client: Client,

    info!(guard.log, "Starting XWayland");

    let (status_child, status_me) = UnixStream::pair().map_err(|_| ())?;
    let (x_wm_x11, x_wm_me) = UnixStream::pair().map_err(|_| ())?;
    let (wl_x11, wl_me) = UnixStream::pair().map_err(|_| ())?;

#[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
    }));

    // all is ready, we can do the fork dance
    let child_pid = match unsafe { fork() } {
        Ok(ForkResult::Parent { child }) => {
            // we are the main smithay process
            child
        }
        Ok(ForkResult::Child) => {
            // we are the first child
            let mut set = signal::SigSet::empty();
            set.add(signal::Signal::SIGUSR1);
            set.add(signal::Signal::SIGCHLD);
            // we can't handle errors here anyway
            let _ = signal::sigprocmask(signal::SigmaskHow::SIG_BLOCK, Some(&set), None);
            match unsafe { fork() } {
                Ok(ForkResult::Parent { child }) => {
                    // When we exit(), we will close() this which wakes up the main process.
                    let _status_child = status_child;
                    // we are still the first child
                    let sig = set.wait();
                    // Parent will wait for us and know from out
                    // exit status if XWayland launch was a success or not =)
                    if let Ok(signal::Signal::SIGCHLD) = sig {
                        // XWayland has exited before being ready
                        let _ = ::nix::sys::wait::waitpid(child, None);
                        unsafe { ::nix::libc::exit(1) };
                    }
                    unsafe { ::nix::libc::exit(0) };
                }
                Ok(ForkResult::Child) => {
                    // we are the second child, we exec xwayland
                    match exec_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) {
                        Ok(x) => match x {},
                        Err(e) => {
                            // well, what can we do ?
                            error!(guard.log, "exec XWayland failed"; "err" => format!("{:?}", e));
                            unsafe { ::nix::libc::exit(1) };
                        }
                    }
                }
                Err(e) => {
                    // well, what can we do ?
                    error!(guard.log, "XWayland second fork failed"; "err" => format!("{:?}", e));
                    unsafe { ::nix::libc::exit(1) };
                }
            }
        }
        Err(e) => {
            error!(guard.log, "XWayland first fork failed"; "err" => format!("{:?}", e));
            return Err(());
        }
    };

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

    guard.instance = Some(XWaylandInstance {
        display_lock: lock,
    if guard.instance.is_some() {
        startup_handler: Some(startup_handler),
        wm_fd: Some(x_wm_me),
        started_at: creation_time,
        child_pid: Some(child_pid),
    });

    Ok(())
    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.
}

fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
    use nix::sys::wait;
    let mut guard = inner.borrow_mut();
    let inner = &mut *guard;
    // instance should never be None at this point
    let instance = inner.instance.as_mut().unwrap();
    let wm = &mut inner.wm;
    // neither the pid
    let pid = instance.child_pid.unwrap();

    // find out if the launch was a success by waiting on the intermediate child
    let success: bool;
    loop {
        match wait::waitpid(pid, None) {
            Ok(wait::WaitStatus::Exited(_, 0)) => {
                // XWayland was correctly started :)
                success = true;
                break;
            }
            Err(NixError::Sys(Errno::EINTR)) => {
                // interupted, retry
                continue;
            }
            _ => {
                // something went wrong :(
                success = false;
                break;
            }
        }
    }

    if success {
        // signal the WM
        info!(inner.log, "XWayland is ready, signaling the WM.");
        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_lock.display()));
    } else {
        error!(
            inner.log,
        &mut self,
        poll: &mut calloop::Poll,
        factory: &mut calloop::TokenFactory,
    ) -> calloop::Result<()> {
        self.channel.register(poll, factory)
    }

    }
}

enum Void {}

/// Exec XWayland with given sockets on given display
///
/// If this returns, that means that something failed
fn exec_xwayland(
    display: u32,
    wayland_socket: UnixStream,
    wm_socket: UnixStream,

impl Inner {
    // Shutdown the XWayland server and cleanup everything
    fn shutdown(&mut self) {
        // don't do anything if not running
        if let Some(instance) = self.instance.take() {
            info!(self.log, "Shutting down XWayland.");
            self.dh
                .backend_handle()
                .kill_client(instance.wayland_client.id(), DisconnectReason::ConnectionClosed);

            // send error occurs if the user dropped the channel... We cannot do much except ignore.
            let _ = self.sender.send(XWaylandEvent::Exited);

            // All connections and lockfiles are cleaned by their destructors

            // Remove DISPLAY from the env
            ::std::env::remove_var("DISPLAY");
            // We do like wlroots:
            // > We do not kill the XWayland process, it dies to broken pipe
            // > after we close our side of the wm/wl fds. This is more reliable
            // > than trying to kill something that might no longer be XWayland.
        }
    }
}

fn xwayland_ready(inner: &Arc<Mutex<Inner>>) {
    // Lots of re-borrowing to please the borrow-checker
    let mut guard = inner.lock().unwrap();
    let guard = &mut *guard;
    info!(guard.log, "XWayland ready");
    // instance should never be None at this point
    let instance = guard.instance.as_mut().unwrap();
    // neither the child_stdout
    let child_stdout = &mut instance.child_stdout;

    // 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!(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.",
            instance.display_lock.display()
        );
        // send error occurs if the user dropped the channel... We cannot do much except ignore.
        let _ = guard.sender.send(XWaylandEvent::Ready {
            connection: instance.wm_fd.take().unwrap(), // This is a bug if None
            client: instance.wayland_client.clone(),
            client_fd: instance.wayland_client_fd,
            display: instance.display_lock.display(),
        });
    } 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 a fork helper process to launch Xwayland (#250)

* xwayland: Add LaunchHelper

Calling fork() in a multi-threaded process is a bad idea. I am getting
crashes at exit() and "man fork" says that only async-signal-safe
functions can be called after forking in a multi-threaded process.
exit() is not async-signal safe. Thus, forking needs to happen before
any threads are created.

This commit adds a LaunchHelper to the public API. This is a handle to a
forked child process. So far this does not do much, but the intention is
to use this later instead of fork()ing directly.

Signed-off-by: Uli Schlachter <346c0ad49ac8427b61b3b55abbe37dc0ebd524f2@znc.in>

* xwayland: Move the fork dance to LaunchHelper

No functional changes intended. This only moves the code over and does
not actually use the LaunchHelper for anything.

Signed-off-by: Uli Schlachter <346c0ad49ac8427b61b3b55abbe37dc0ebd524f2@znc.in>

* Move checking the launch status to LaunchHelper

This should again have no functional changes, but the error log output
might change slightly, since an IOError instead of a NixError is
returned.

Signed-off-by: Uli Schlachter <346c0ad49ac8427b61b3b55abbe37dc0ebd524f2@znc.in>

* xwayland: Set $DISPLAY earlier

This way, the WM already gets the correct value of $DISPLAY.

Signed-off-by: Uli Schlachter <346c0ad49ac8427b61b3b55abbe37dc0ebd524f2@znc.in>

* xwayland: Use the launch helper process

FD passing is now used to give the display number and the sockets to the
launch helper process. That process then forks of the actual process
that spawns Xwayland. Once Xwayland started, this is reported back with
another write on the pipe.

Signed-off-by: Uli Schlachter <346c0ad49ac8427b61b3b55abbe37dc0ebd524f2@znc.in>

* Add to the comment explaining Xwayland startup

Signed-off-by: Uli Schlachter <346c0ad49ac8427b61b3b55abbe37dc0ebd524f2@znc.in>

* Please rustfmt

Signed-off-by: Uli Schlachter <346c0ad49ac8427b61b3b55abbe37dc0ebd524f2@znc.in>

* Make clippy happy

Signed-off-by: Uli Schlachter <346c0ad49ac8427b61b3b55abbe37dc0ebd524f2@znc.in>
<DFF> @@ -23,6 +23,11 @@
  *
  * cf https://github.com/swaywm/wlroots/blob/master/xwayland/xwayland.c
  *
+ * Since fork is not safe to call in a multi-threaded process, a process is
+ * forked of early (LaunchHelper). Via a shared FD, a command to actually launch
+ * Xwayland can be sent to that process. The process then does the fork and
+ * reports back when Xwayland successfully started (=SIGUSR1 was received) with
+ * another write on the pipe.
  */
 use std::{
     any::Any,
@@ -30,25 +35,26 @@ use std::{
     env,
     ffi::CString,
     os::unix::{
-        io::{AsRawFd, IntoRawFd},
+        io::{AsRawFd, IntoRawFd, RawFd},
         net::UnixStream,
     },
     rc::Rc,
     sync::Arc,
 };
 
-use calloop::{generic::Generic, Interest, LoopHandle, Mode, Source};
-
-use nix::{
-    errno::Errno,
-    sys::signal,
-    unistd::{fork, ForkResult, Pid},
-    Error as NixError, Result as NixResult,
+use calloop::{
+    generic::{Fd, Generic},
+    Interest, LoopHandle, Mode, Source,
 };
 
+use nix::Result as NixResult;
+
 use wayland_server::{Client, Display, Filter};
 
-use super::x11_sockets::{prepare_x11_sockets, X11Lock};
+use super::{
+    x11_sockets::{prepare_x11_sockets, X11Lock},
+    LaunchHelper,
+};
 
 /// The XWayland handle
 pub struct XWayland<WM: XWindowManager> {
@@ -81,6 +87,7 @@ 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>>,
@@ -95,7 +102,7 @@ impl<WM: XWindowManager + 'static> XWayland<WM> {
             source_maker: Box::new(move |inner, fd| {
                 handle
                     .insert_source(
-                        Generic::new(fd, Interest::Readable, Mode::Level),
+                        Generic::new(Fd(fd), Interest::Readable, Mode::Level),
                         move |evt, _, _| {
                             debug_assert!(evt.readable);
                             xwayland_ready(&inner);
@@ -106,6 +113,7 @@ impl<WM: XWindowManager + 'static> XWayland<WM> {
             }),
             wayland_display: display,
             instance: None,
+            helper,
             log: log.new(o!("smithay_module" => "XWayland")),
         }));
         launch(&inner, data)?;
@@ -122,14 +130,12 @@ impl<WM: XWindowManager> Drop for XWayland<WM> {
 struct XWaylandInstance {
     display_lock: X11Lock,
     wayland_client: Client,
-    startup_handler: Option<Source<Generic<UnixStream>>>,
+    startup_handler: Option<Source<Generic<Fd>>>,
     wm_fd: Option<UnixStream>,
     started_at: ::std::time::Instant,
-    child_pid: Option<Pid>,
 }
 
-type SourceMaker<WM> =
-    dyn FnMut(Rc<RefCell<Inner<WM>>>, UnixStream) -> Result<Source<Generic<UnixStream>>, ()>;
+type SourceMaker<WM> = dyn FnMut(Rc<RefCell<Inner<WM>>>, RawFd) -> Result<Source<Generic<Fd>>, ()>;
 
 // Inner implementation of the XWayland manager
 struct Inner<WM: XWindowManager> {
@@ -137,7 +143,8 @@ struct Inner<WM: XWindowManager> {
     source_maker: Box<SourceMaker<WM>>,
     wayland_display: Rc<RefCell<Display>>,
     instance: Option<XWaylandInstance>,
-    kill_source: Box<dyn Fn(Source<Generic<UnixStream>>)>,
+    kill_source: Box<dyn Fn(Source<Generic<Fd>>)>,
+    helper: LaunchHelper,
     log: ::slog::Logger,
 }
 
@@ -155,7 +162,6 @@ fn launch<WM: XWindowManager + 'static, T: Any>(
 
     info!(guard.log, "Starting XWayland");
 
-    let (status_child, status_me) = UnixStream::pair().map_err(|_| ())?;
     let (x_wm_x11, x_wm_me) = UnixStream::pair().map_err(|_| ())?;
     let (wl_x11, wl_me) = UnixStream::pair().map_err(|_| ())?;
 
@@ -179,58 +185,16 @@ fn launch<WM: XWindowManager + 'static, T: Any>(
     }));
 
     // all is ready, we can do the fork dance
-    let child_pid = match unsafe { fork() } {
-        Ok(ForkResult::Parent { child }) => {
-            // we are the main smithay process
-            child
-        }
-        Ok(ForkResult::Child) => {
-            // we are the first child
-            let mut set = signal::SigSet::empty();
-            set.add(signal::Signal::SIGUSR1);
-            set.add(signal::Signal::SIGCHLD);
-            // we can't handle errors here anyway
-            let _ = signal::sigprocmask(signal::SigmaskHow::SIG_BLOCK, Some(&set), None);
-            match unsafe { fork() } {
-                Ok(ForkResult::Parent { child }) => {
-                    // When we exit(), we will close() this which wakes up the main process.
-                    let _status_child = status_child;
-                    // we are still the first child
-                    let sig = set.wait();
-                    // Parent will wait for us and know from out
-                    // exit status if XWayland launch was a success or not =)
-                    if let Ok(signal::Signal::SIGCHLD) = sig {
-                        // XWayland has exited before being ready
-                        let _ = ::nix::sys::wait::waitpid(child, None);
-                        unsafe { ::nix::libc::exit(1) };
-                    }
-                    unsafe { ::nix::libc::exit(0) };
-                }
-                Ok(ForkResult::Child) => {
-                    // we are the second child, we exec xwayland
-                    match exec_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) {
-                        Ok(x) => match x {},
-                        Err(e) => {
-                            // well, what can we do ?
-                            error!(guard.log, "exec XWayland failed"; "err" => format!("{:?}", e));
-                            unsafe { ::nix::libc::exit(1) };
-                        }
-                    }
-                }
-                Err(e) => {
-                    // well, what can we do ?
-                    error!(guard.log, "XWayland second fork failed"; "err" => format!("{:?}", e));
-                    unsafe { ::nix::libc::exit(1) };
-                }
-            }
-        }
+    match guard.helper.launch(lock.display(), wl_x11, x_wm_x11, &x_fds) {
+        Ok(()) => {}
         Err(e) => {
-            error!(guard.log, "XWayland first fork failed"; "err" => format!("{:?}", e));
+            error!(guard.log, "Could not initiate launch of Xwayland"; "err" => format!("{:?}", e));
             return Err(());
         }
-    };
+    }
 
-    let startup_handler = (&mut *guard.source_maker)(inner.clone(), status_me)?;
+    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,
@@ -238,7 +202,6 @@ fn launch<WM: XWindowManager + 'static, T: Any>(
         startup_handler: Some(startup_handler),
         wm_fd: Some(x_wm_me),
         started_at: creation_time,
-        child_pid: Some(child_pid),
     });
 
     Ok(())
@@ -290,46 +253,30 @@ fn client_destroy<WM: XWindowManager + 'static, T: Any>(map: &::wayland_server::
 }
 
 fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
-    use nix::sys::wait;
     let mut guard = inner.borrow_mut();
     let inner = &mut *guard;
     // instance should never be None at this point
     let instance = inner.instance.as_mut().unwrap();
     let wm = &mut inner.wm;
-    // neither the pid
-    let pid = instance.child_pid.unwrap();
-
-    // find out if the launch was a success by waiting on the intermediate child
-    let success: bool;
-    loop {
-        match wait::waitpid(pid, None) {
-            Ok(wait::WaitStatus::Exited(_, 0)) => {
-                // XWayland was correctly started :)
-                success = true;
-                break;
-            }
-            Err(NixError::Sys(Errno::EINTR)) => {
-                // interupted, retry
-                continue;
-            }
-            _ => {
-                // something went wrong :(
-                success = false;
-                break;
-            }
+
+    let success = match inner.helper.was_launch_succesful() {
+        Ok(s) => s,
+        Err(e) => {
+            error!(inner.log, "Checking launch status failed"; "err" => format!("{:?}", e));
+            false
         }
-    }
+    };
 
     if success {
+        // setup the environemnt
+        ::std::env::set_var("DISPLAY", format!(":{}", instance.display_lock.display()));
+
         // signal the WM
         info!(inner.log, "XWayland is ready, signaling the WM.");
         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_lock.display()));
     } else {
         error!(
             inner.log,
@@ -343,12 +290,12 @@ fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
     }
 }
 
-enum Void {}
+pub(crate) enum Void {}
 
 /// Exec XWayland with given sockets on given display
 ///
 /// If this returns, that means that something failed
-fn exec_xwayland(
+pub(crate) fn exec_xwayland(
     display: u32,
     wayland_socket: UnixStream,
     wm_socket: UnixStream,

XWayland: Use a fork helper process to launch Xwayland (#250)