From d2cbadc670843e366e9ce15ee772ebdf59056752 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 30 Apr 2018 21:28:17 +0200 Subject: [PATCH 1/5] xwayland: core infrastructure --- Cargo.toml | 12 +- src/lib.rs | 3 + src/xwayland/mod.rs | 4 + src/xwayland/x11_sockets.rs | 136 ++++++++++++ src/xwayland/xserver.rs | 401 ++++++++++++++++++++++++++++++++++++ 5 files changed, 550 insertions(+), 6 deletions(-) create mode 100644 src/xwayland/mod.rs create mode 100644 src/xwayland/x11_sockets.rs create mode 100644 src/xwayland/xserver.rs diff --git a/Cargo.toml b/Cargo.toml index fae0421..93f6b2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,15 +7,15 @@ description = "Smithay is a library for writing wayland compositors." repository = "https://github.com/Smithay/smithay" [dependencies] -wayland-server = "0.20.1" -wayland-sys = "0.20.1" +wayland-server = "0.20.2" +wayland-sys = "0.20.2" nix = "0.10.0" xkbcommon = "0.2.1" tempfile = "2.1.5" slog = { version = "2.1.1" } slog-stdlog = "3.0.2" libloading = "0.4.0" -wayland-client = { version = "0.20.1", optional = true } +wayland-client = { version = "0.20.2", optional = true } winit = { version = "0.10.0", optional = true } drm = { version = "^0.3.1", optional = true } gbm = { version = "^0.4.0", optional = true, default-features = false, features = ["drm-support"] } @@ -24,7 +24,7 @@ input = { version = "0.4.0", optional = true } udev = { version = "0.2.0", optional = true } dbus = { version = "0.6.1", optional = true } systemd = { version = "^0.2.0", optional = true } -wayland-protocols = { version = "0.20.1", features = ["unstable_protocols", "server"] } +wayland-protocols = { version = "0.20.2", features = ["unstable_protocols", "server"] } image = "0.17.0" error-chain = "0.11.0" lazy_static = "1.0.0" @@ -38,7 +38,7 @@ slog-async = "2.2" rand = "0.3" [features] -default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium"] +default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium", "xwayland"] backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"] backend_drm = ["drm", "gbm"] backend_libinput = ["input"] @@ -47,4 +47,4 @@ backend_session_udev = ["udev", "backend_session"] backend_session_logind = ["dbus", "systemd", "backend_session"] backend_udev = ["udev", "backend_drm", "backend_session_udev"] renderer_glium = ["glium"] - +xwayland = [] diff --git a/src/lib.rs b/src/lib.rs index ec1d923..9366821 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,9 @@ pub mod backend; pub mod wayland; pub mod utils; +#[cfg(feature = "xwayland")] +pub mod xwayland; + fn slog_or_stdlog(logger: L) -> ::slog::Logger where L: Into>, diff --git a/src/xwayland/mod.rs b/src/xwayland/mod.rs new file mode 100644 index 0000000..5e21dd1 --- /dev/null +++ b/src/xwayland/mod.rs @@ -0,0 +1,4 @@ +mod xserver; +mod x11_sockets; + +pub use self::xserver::{XWayland, XWindowManager}; diff --git a/src/xwayland/x11_sockets.rs b/src/xwayland/x11_sockets.rs new file mode 100644 index 0000000..3b06538 --- /dev/null +++ b/src/xwayland/x11_sockets.rs @@ -0,0 +1,136 @@ +use std::io::{Read, Write}; +use std::os::unix::io::RawFd; + +use nix::{Error as NixError, Result as NixResult}; +use nix::errno::Errno; +use nix::sys::socket; + +pub(crate) fn make_pair() -> Result<(RawFd, RawFd), ()> { + socket::socketpair( + socket::AddressFamily::Unix, + socket::SockType::Stream, + None, + socket::SockFlag::SOCK_CLOEXEC, + ).map_err(|_| ()) +} + +/// Find a free X11 display slot and setup +pub(crate) fn prepare_x11_sockets() -> Result<(u32, [RawFd; 2]), ()> { + for d in 0..33 { + // if fails, try the next one + if let Err(()) = grab_lockfile(d) { + continue; + } + // we got a lockfile, try and create the socket + if let Ok(fds) = open_x11_sockets_for_display(d) { + return Ok((d, fds)); + } + // creating the sockets failed, for some readon ? + // release the lockfile and try with the next + release_lockfile(d); + } + // If we reach here, all values from 0 to 32 failed + // we need to stop trying at some point + return Err(()); +} + +/// Remove the X11 sockets for a given display number +pub(crate) fn cleanup_x11_sockets(display: u32) { + let _ = ::std::fs::remove_file(format!("/tmp/.X11-unix/X{}", display)); + let _ = ::std::fs::remove_file(format!("/tmp/.X{}-lock", display)); +} + +/// Try to grab a lockfile for given X display number +fn grab_lockfile(display: u32) -> Result<(), ()> { + let filename = format!("/tmp/.X{}-lock", display); + let lockfile = ::std::fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(&filename); + match lockfile { + Ok(mut file) => { + // we got it, write our PID in it and we're good + let ret = file.write_fmt(format_args!("{:>10}", ::nix::unistd::Pid::this())); + if let Err(_) = ret { + // write to the file failed ? we abandon + ::std::mem::drop(file); + let _ = ::std::fs::remove_file(&filename); + return Err(()); + } else { + // we got the lockfile and wrote our pid to it, all is good + return Ok(()); + } + } + Err(_) => { + // we could not open the file, now we try to read it + // and if it contains the pid of a process that no longer + // exist (so if a previous x server claimed it and did not + // exit gracefully and remove it), we claim it + // if we can't open it, give up + let mut file = ::std::fs::File::open(&filename).map_err(|_| ())?; + let mut spid = [0u8; 11]; + file.read_exact(&mut spid).map_err(|_| ())?; + ::std::mem::drop(file); + let pid = ::nix::unistd::Pid::from_raw(::std::str::from_utf8(&spid) + .map_err(|_| ())? + .trim() + .parse::() + .map_err(|_| ())?); + if let Err(NixError::Sys(Errno::ESRCH)) = ::nix::sys::signal::kill(pid, None) { + // no process whose pid equals the contents of the lockfile exists + // remove the lockfile and try grabbing it again + let _ = ::std::fs::remove_file(filename); + return grab_lockfile(display); + } + // if we reach here, this lockfile exists and is probably in use, give up + return Err(()); + } + } +} + +/// Release an X11 lockfile +fn release_lockfile(display: u32) { + let filename = format!("/tmp/.X{}-lock", display); + let _ = ::std::fs::remove_file(filename); +} + +/// Open the two unix sockets an X server listens on +/// +/// SHould only be done after the associated lockfile is aquired! +fn open_x11_sockets_for_display(display: u32) -> NixResult<[RawFd; 2]> { + let fs_socket = open_socket( + socket::UnixAddr::new(format!("/tmp/.X11-unix/X{}", display).as_bytes()).unwrap(), // We know this path is not to long, this unwrap cannot fail + )?; + let ret = open_socket( + socket::UnixAddr::new_abstract(format!("/tmp/.X11-unix/X{}", display).as_bytes()).unwrap(), // We know this path is not to long, this unwrap cannot fail + ); + match ret { + Ok(abstract_socket) => Ok([fs_socket, abstract_socket]), + Err(e) => { + // close the first socket and return the error + let _ = ::nix::unistd::close(fs_socket); + Err(e) + } + } +} + +/// Open an unix socket for listening and bind it to given path +fn open_socket(addr: socket::UnixAddr) -> NixResult { + // create an unix stream socket + let fd = socket::socket( + socket::AddressFamily::Unix, + socket::SockType::Stream, + socket::SockFlag::SOCK_CLOEXEC, + None, + )?; + // bind it to requested address + if let Err(e) = socket::bind(fd, &socket::SockAddr::Unix(addr)) { + let _ = ::nix::unistd::close(fd); + return Err(e); + } + if let Err(e) = socket::listen(fd, 1) { + let _ = ::nix::unistd::close(fd); + return Err(e); + } + Ok(fd) +} diff --git a/src/xwayland/xserver.rs b/src/xwayland/xserver.rs new file mode 100644 index 0000000..9a788b5 --- /dev/null +++ b/src/xwayland/xserver.rs @@ -0,0 +1,401 @@ +/* + * 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 ouserlves 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 respectly: + * - /tmp/.X11-unix/X$D + * - @/tmp/.X11-unix/X$D (abstract socket) + * + * The XWayland server is spawned via fork+exec. + * -> wlroot does a double-fork while weston a single one, why ?? + * -> https://stackoverflow.com/questions/881388/ + * -> once it is started, it sends us a SIGUSR1, we need to setup a listener + * for it and when we receive it we can launch the WM + * -> we need to track if the Xwayland crashes, to restart it + * + * cf https://github.com/swaywm/wlroots/blob/master/xwayland/xwayland.c + * + */ +use std::cell::RefCell; +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; +use nix::unistd::{close, fork, ForkResult, Pid}; +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}; + +/// The XWayland handle +pub struct XWayland { + inner: Rc>>, +} + +/// Trait to be implemented by you WM for Xwayland +/// +/// This is a very low-level trait, only notifying you +/// when the connection with XWayland is up, or when +/// it terminates. +/// +/// You WM must be able handle the XWayland server connecting +/// then disconnecting several time in a row, but only a single +/// connection will be active at any given time. +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); +} + +impl XWayland { + /// Start the XWayland server + pub fn init(wm: WM, token: LoopToken, display: Rc>) -> Result, ()> { + let inner = Rc::new(RefCell::new(Inner { + wm, + token, + wayland_display: display, + instance: None, + })); + launch(&inner)?; + Ok(XWayland { inner }) + } +} + +impl Drop for XWayland { + fn drop(&mut self) { + self.inner.borrow_mut().shutdown(); + } +} + +struct XWaylandInstance { + display: u32, + wayland_client: Client, + sigusr1_handler: Option>, + wm_fd: RawFd, + started_at: ::std::time::Instant, + child_pid: Option, +} + +// Inner implementation of the XWayland manager +struct Inner { + wm: WM, + token: LoopToken, + wayland_display: Rc>, + instance: Option, +} + +// Launch an XWayland server +// +// Does nothing if there is already a launched instance +fn launch(inner: &Rc>>) -> Result<(), ()> { + let mut guard = inner.borrow_mut(); + if guard.instance.is_some() { + 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 + + // record launch time + 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::); + + // setup the SIGUSR1 handler + let my_inner = inner.clone(); + let sigusr1_handler = match guard + .token + .add_signal_event_source(signal::Signal::SIGUSR1, move |_, ()| { + xwayland_ready(&my_inner) + }) { + 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(()); + } + }; + + // all is ready, we can do the fork dance + let child_pid = match fork() { + Ok(ForkResult::Parent { child }) => { + // we are the main smithay process + child + } + Ok(ForkResult::Child) => { + // we are the first child + let ppid = Pid::parent(); + 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 fork() { + Ok(ForkResult::Parent { child }) => { + // we are still the first child + let sig = set.wait(); + // send USR1 to parent + let _ = signal::kill(ppid, signal::Signal::SIGUSR1); + // 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(display, wl_x11, x_wm_x11, &x_fds) { + Ok(x) => match x {}, + Err(e) => { + // well, what can we do ? + eprintln!("[smithay] exec XWayland failed: {:?}", e); + unsafe { ::nix::libc::exit(1) }; + } + } + } + Err(e) => { + // well, what can we do ? + eprintln!("[smithay] XWayland second fork failed: {:?}", e); + unsafe { ::nix::libc::exit(1) }; + } + } + } + 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), + }); + + Ok(()) +} + +impl Inner { + // Shutdown the XWayland server and cleanup everything + fn shutdown(&mut self) { + // don't do anything if not running + if let Some(mut instance) = self.instance.take() { + self.wm.xwayland_exited(); + // kill the client + instance.wayland_client.kill(); + // remove the event source + 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"); + // 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 client_destroy(data: *mut ()) { + let inner = unsafe { Rc::from_raw(data as *const () as *const RefCell>) }; + + // shutdown the server + let started_at = inner.borrow().instance.as_ref().map(|i| i.started_at); + inner.borrow_mut().shutdown(); + + // restart it, unless we really just started it, if it crashes right + // at startup there is no point + if started_at.map(|t| t.elapsed().as_secs()).unwrap_or(10) > 5 { + let _ = launch(&inner); + } +} + +fn xwayland_ready(inner: &Rc>>) { + 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 mut 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 + wm.xwayland_ready(instance.wm_fd, instance.wayland_client.clone()); + + // setup the environemnt + ::std::env::set_var("DISPLAY", format!(":{}", instance.display)); + } + + // in all cases, cleanup + if let Some(s) = instance.sigusr1_handler.take() { + s.remove(); + } +} + +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: RawFd, + wm_socket: RawFd, + listen_sockets: &[RawFd], +) -> NixResult { + // 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)).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() { + 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)); + + // 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)?; + // 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(fd: RawFd) -> NixResult<()> { + use nix::fcntl::{fcntl, FcntlArg, FdFlag}; + fcntl(fd, FcntlArg::F_SETFD(FdFlag::empty()))?; + Ok(()) +} From 5ae34d261347ec7fc15ca41cd536c3d350fe2a8b Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 1 May 2018 11:56:32 +0200 Subject: [PATCH 2/5] xwayland: Use Rust's UnixStream instead of RawFd THis allows us to take advantage of RAII for cleanup, among others. --- src/xwayland/x11_sockets.rs | 41 +++++---------- src/xwayland/xserver.rs | 100 +++++++++++------------------------- 2 files changed, 44 insertions(+), 97 deletions(-) diff --git a/src/xwayland/x11_sockets.rs b/src/xwayland/x11_sockets.rs index 3b06538..491b365 100644 --- a/src/xwayland/x11_sockets.rs +++ b/src/xwayland/x11_sockets.rs @@ -1,21 +1,13 @@ use std::io::{Read, Write}; -use std::os::unix::io::RawFd; +use std::os::unix::io::FromRawFd; +use std::os::unix::net::UnixStream; use nix::{Error as NixError, Result as NixResult}; use nix::errno::Errno; use nix::sys::socket; -pub(crate) fn make_pair() -> Result<(RawFd, RawFd), ()> { - socket::socketpair( - socket::AddressFamily::Unix, - socket::SockType::Stream, - None, - socket::SockFlag::SOCK_CLOEXEC, - ).map_err(|_| ()) -} - /// Find a free X11 display slot and setup -pub(crate) fn prepare_x11_sockets() -> Result<(u32, [RawFd; 2]), ()> { +pub(crate) fn prepare_x11_sockets() -> Result<(u32, [UnixStream; 2]), ()> { for d in 0..33 { // if fails, try the next one if let Err(()) = grab_lockfile(d) { @@ -97,25 +89,18 @@ fn release_lockfile(display: u32) { /// Open the two unix sockets an X server listens on /// /// SHould only be done after the associated lockfile is aquired! -fn open_x11_sockets_for_display(display: u32) -> NixResult<[RawFd; 2]> { - let fs_socket = open_socket( - socket::UnixAddr::new(format!("/tmp/.X11-unix/X{}", display).as_bytes()).unwrap(), // We know this path is not to long, this unwrap cannot fail - )?; - let ret = open_socket( - socket::UnixAddr::new_abstract(format!("/tmp/.X11-unix/X{}", display).as_bytes()).unwrap(), // We know this path is not to long, this unwrap cannot fail - ); - match ret { - Ok(abstract_socket) => Ok([fs_socket, abstract_socket]), - Err(e) => { - // close the first socket and return the error - let _ = ::nix::unistd::close(fs_socket); - Err(e) - } - } +fn open_x11_sockets_for_display(display: u32) -> NixResult<[UnixStream; 2]> { + let path = format!("/tmp/.X11-unix/X{}", display); + // We know this path is not to long, these unwrap cannot fail + let fs_addr = socket::UnixAddr::new(path.as_bytes()).unwrap(); + let abs_addr = socket::UnixAddr::new_abstract(path.as_bytes()).unwrap(); + let fs_socket = open_socket(fs_addr)?; + let abstract_socket = open_socket(abs_addr)?; + Ok([fs_socket, abstract_socket]) } /// Open an unix socket for listening and bind it to given path -fn open_socket(addr: socket::UnixAddr) -> NixResult { +fn open_socket(addr: socket::UnixAddr) -> NixResult { // create an unix stream socket let fd = socket::socket( socket::AddressFamily::Unix, @@ -132,5 +117,5 @@ fn open_socket(addr: socket::UnixAddr) -> NixResult { let _ = ::nix::unistd::close(fd); return Err(e); } - Ok(fd) + Ok(unsafe { FromRawFd::from_raw_fd(fd) }) } diff --git a/src/xwayland/xserver.rs b/src/xwayland/xserver.rs index 9a788b5..a57a92b 100644 --- a/src/xwayland/xserver.rs +++ b/src/xwayland/xserver.rs @@ -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 { @@ -57,8 +58,8 @@ pub struct XWayland { 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>, - wm_fd: RawFd, + wm_fd: Option, started_at: ::std::time::Instant, child_pid: Option, } @@ -109,32 +110,10 @@ fn launch(inner: &Rc>>) -> 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(inner: &Rc>>) -> 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::); @@ -155,14 +134,6 @@ fn launch(inner: &Rc>>) -> 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(inner: &Rc>>) -> 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 Inner { 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(inner: &Rc>>) { 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 { // 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(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(()) } From 2d8653d9d75ecd014623da2ac92612064f1a0c1a Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Tue, 1 May 2018 12:08:01 +0200 Subject: [PATCH 3/5] xwayland: take advantage of RAII for X11 lockfile --- src/xwayland/x11_sockets.rs | 121 +++++++++++++++++++----------------- src/xwayland/xserver.rs | 47 +++++++------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/src/xwayland/x11_sockets.rs b/src/xwayland/x11_sockets.rs index 491b365..1791b68 100644 --- a/src/xwayland/x11_sockets.rs +++ b/src/xwayland/x11_sockets.rs @@ -7,19 +7,15 @@ use nix::errno::Errno; use nix::sys::socket; /// Find a free X11 display slot and setup -pub(crate) fn prepare_x11_sockets() -> Result<(u32, [UnixStream; 2]), ()> { +pub(crate) fn prepare_x11_sockets() -> Result<(X11Lock, [UnixStream; 2]), ()> { for d in 0..33 { // if fails, try the next one - if let Err(()) = grab_lockfile(d) { - continue; + if let Ok(lock) = X11Lock::grab(d) { + // we got a lockfile, try and create the socket + if let Ok(sockets) = open_x11_sockets_for_display(d) { + return Ok((lock, sockets)); + } } - // we got a lockfile, try and create the socket - if let Ok(fds) = open_x11_sockets_for_display(d) { - return Ok((d, fds)); - } - // creating the sockets failed, for some readon ? - // release the lockfile and try with the next - release_lockfile(d); } // If we reach here, all values from 0 to 32 failed // we need to stop trying at some point @@ -27,63 +23,72 @@ pub(crate) fn prepare_x11_sockets() -> Result<(u32, [UnixStream; 2]), ()> { } /// Remove the X11 sockets for a given display number -pub(crate) fn cleanup_x11_sockets(display: u32) { - let _ = ::std::fs::remove_file(format!("/tmp/.X11-unix/X{}", display)); - let _ = ::std::fs::remove_file(format!("/tmp/.X{}-lock", display)); +pub(crate) fn cleanup_x11_sockets(display: u32) {} + +pub(crate) struct X11Lock { + display: u32, } -/// Try to grab a lockfile for given X display number -fn grab_lockfile(display: u32) -> Result<(), ()> { - let filename = format!("/tmp/.X{}-lock", display); - let lockfile = ::std::fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(&filename); - match lockfile { - Ok(mut file) => { - // we got it, write our PID in it and we're good - let ret = file.write_fmt(format_args!("{:>10}", ::nix::unistd::Pid::this())); - if let Err(_) = ret { - // write to the file failed ? we abandon +impl X11Lock { + /// Try to grab a lockfile for given X display number + fn grab(display: u32) -> Result { + let filename = format!("/tmp/.X{}-lock", display); + let lockfile = ::std::fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(&filename); + match lockfile { + Ok(mut file) => { + // we got it, write our PID in it and we're good + let ret = file.write_fmt(format_args!("{:>10}", ::nix::unistd::Pid::this())); + if let Err(_) = ret { + // write to the file failed ? we abandon + ::std::mem::drop(file); + let _ = ::std::fs::remove_file(&filename); + return Err(()); + } else { + // we got the lockfile and wrote our pid to it, all is good + return Ok(X11Lock { display }); + } + } + Err(_) => { + // we could not open the file, now we try to read it + // and if it contains the pid of a process that no longer + // exist (so if a previous x server claimed it and did not + // exit gracefully and remove it), we claim it + // if we can't open it, give up + let mut file = ::std::fs::File::open(&filename).map_err(|_| ())?; + let mut spid = [0u8; 11]; + file.read_exact(&mut spid).map_err(|_| ())?; ::std::mem::drop(file); - let _ = ::std::fs::remove_file(&filename); + let pid = ::nix::unistd::Pid::from_raw(::std::str::from_utf8(&spid) + .map_err(|_| ())? + .trim() + .parse::() + .map_err(|_| ())?); + if let Err(NixError::Sys(Errno::ESRCH)) = ::nix::sys::signal::kill(pid, None) { + // no process whose pid equals the contents of the lockfile exists + // remove the lockfile and try grabbing it again + let _ = ::std::fs::remove_file(filename); + return X11Lock::grab(display); + } + // if we reach here, this lockfile exists and is probably in use, give up return Err(()); - } else { - // we got the lockfile and wrote our pid to it, all is good - return Ok(()); } } - Err(_) => { - // we could not open the file, now we try to read it - // and if it contains the pid of a process that no longer - // exist (so if a previous x server claimed it and did not - // exit gracefully and remove it), we claim it - // if we can't open it, give up - let mut file = ::std::fs::File::open(&filename).map_err(|_| ())?; - let mut spid = [0u8; 11]; - file.read_exact(&mut spid).map_err(|_| ())?; - ::std::mem::drop(file); - let pid = ::nix::unistd::Pid::from_raw(::std::str::from_utf8(&spid) - .map_err(|_| ())? - .trim() - .parse::() - .map_err(|_| ())?); - if let Err(NixError::Sys(Errno::ESRCH)) = ::nix::sys::signal::kill(pid, None) { - // no process whose pid equals the contents of the lockfile exists - // remove the lockfile and try grabbing it again - let _ = ::std::fs::remove_file(filename); - return grab_lockfile(display); - } - // if we reach here, this lockfile exists and is probably in use, give up - return Err(()); - } + } + + pub(crate) fn display(&self) -> u32 { + self.display } } -/// Release an X11 lockfile -fn release_lockfile(display: u32) { - let filename = format!("/tmp/.X{}-lock", display); - let _ = ::std::fs::remove_file(filename); +impl Drop for X11Lock { + fn drop(&mut self) { + // Cleanup all the X11 files + let _ = ::std::fs::remove_file(format!("/tmp/.X11-unix/X{}", self.display)); + let _ = ::std::fs::remove_file(format!("/tmp/.X{}-lock", self.display)); + } } /// Open the two unix sockets an X server listens on diff --git a/src/xwayland/xserver.rs b/src/xwayland/xserver.rs index a57a92b..e8afe5b 100644 --- a/src/xwayland/xserver.rs +++ b/src/xwayland/xserver.rs @@ -28,18 +28,18 @@ use std::cell::RefCell; use std::rc::Rc; use std::env; use std::ffi::CString; -use std::os::unix::io::{RawFd, AsRawFd, IntoRawFd}; +use std::os::unix::io::{AsRawFd, IntoRawFd}; use std::os::unix::net::UnixStream; use nix::{Error as NixError, Result as NixResult}; use nix::errno::Errno; -use nix::unistd::{close, fork, ForkResult, Pid}; +use nix::unistd::{fork, ForkResult, Pid}; use nix::sys::signal; use wayland_server::{Client, Display, LoopToken}; use wayland_server::sources::{SignalEvent, Source}; -use super::x11_sockets::{cleanup_x11_sockets, prepare_x11_sockets}; +use super::x11_sockets::{X11Lock, prepare_x11_sockets}; /// The XWayland handle pub struct XWayland { @@ -85,7 +85,7 @@ impl Drop for XWayland { } struct XWaylandInstance { - display: u32, + display_lock: X11Lock, wayland_client: Client, sigusr1_handler: Option>, wm_fd: Option, @@ -113,7 +113,7 @@ fn launch(inner: &Rc>>) -> Resul let (x_wm_x11, x_wm_me) = UnixStream::pair().map_err(|_| ())?; let (wl_x11, wl_me) = UnixStream::pair().map_err(|_| ())?; - let (display, x_fds) = prepare_x11_sockets()?; + let (lock, x_fds) = prepare_x11_sockets()?; // we have now created all the required sockets @@ -121,23 +121,23 @@ fn launch(inner: &Rc>>) -> 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.into_raw_fd()) }; + 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::); // setup the SIGUSR1 handler let my_inner = inner.clone(); - let sigusr1_handler = match guard + let sigusr1_handler = guard .token .add_signal_event_source(signal::Signal::SIGUSR1, move |_, ()| { xwayland_ready(&my_inner) - }) { - Ok(v) => v, - Err(_) => { - cleanup_x11_sockets(display); - return Err(()); - } - }; + }) + .map_err(|_| ())?; // all is ready, we can do the fork dance let child_pid = match fork() { @@ -170,7 +170,7 @@ fn launch(inner: &Rc>>) -> Resul } Ok(ForkResult::Child) => { // we are the second child, we exec xwayland - match exec_xwayland(display, wl_x11, x_wm_x11, &x_fds) { + match exec_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) { Ok(x) => match x {}, Err(e) => { // well, what can we do ? @@ -188,14 +188,12 @@ fn launch(inner: &Rc>>) -> Resul } Err(e) => { eprintln!("[smithay] XWayland first fork failed: {:?}", e); - // fork failed ? cleanup - cleanup_x11_sockets(display); return Err(()); } }; guard.instance = Some(XWaylandInstance { - display, + display_lock: lock, wayland_client: client, sigusr1_handler: Some(sigusr1_handler), wm_fd: Some(x_wm_me), @@ -218,12 +216,9 @@ impl Inner { if let Some(s) = instance.sigusr1_handler.take() { s.remove(); } - // 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); + // All connexions 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 @@ -282,11 +277,11 @@ fn xwayland_ready(inner: &Rc>>) { // signal the WM wm.xwayland_ready( instance.wm_fd.take().unwrap(), // This is a bug if None - instance.wayland_client.clone() + instance.wayland_client.clone(), ); // setup the environemnt - ::std::env::set_var("DISPLAY", format!(":{}", instance.display)); + ::std::env::set_var("DISPLAY", format!(":{}", instance.display_lock.display())); } // in all cases, cleanup From ff63209a17362dee13bdb9de17ee306f53adb94d Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 2 May 2018 15:54:24 +0200 Subject: [PATCH 4/5] XWayland: add logging --- src/xwayland/x11_sockets.rs | 35 ++++++++++++++++++++++------------ src/xwayland/xserver.rs | 38 +++++++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/xwayland/x11_sockets.rs b/src/xwayland/x11_sockets.rs index 1791b68..2fb4486 100644 --- a/src/xwayland/x11_sockets.rs +++ b/src/xwayland/x11_sockets.rs @@ -7,10 +7,10 @@ use nix::errno::Errno; use nix::sys::socket; /// Find a free X11 display slot and setup -pub(crate) fn prepare_x11_sockets() -> Result<(X11Lock, [UnixStream; 2]), ()> { +pub(crate) fn prepare_x11_sockets(log: ::slog::Logger) -> Result<(X11Lock, [UnixStream; 2]), ()> { for d in 0..33 { // if fails, try the next one - if let Ok(lock) = X11Lock::grab(d) { + if let Ok(lock) = X11Lock::grab(d, log.clone()) { // we got a lockfile, try and create the socket if let Ok(sockets) = open_x11_sockets_for_display(d) { return Ok((lock, sockets)); @@ -22,16 +22,15 @@ pub(crate) fn prepare_x11_sockets() -> Result<(X11Lock, [UnixStream; 2]), ()> { return Err(()); } -/// Remove the X11 sockets for a given display number -pub(crate) fn cleanup_x11_sockets(display: u32) {} - pub(crate) struct X11Lock { display: u32, + log: ::slog::Logger, } impl X11Lock { /// Try to grab a lockfile for given X display number - fn grab(display: u32) -> Result { + fn grab(display: u32, log: ::slog::Logger) -> Result { + debug!(log, "Attempting to aquire an X11 display lock"; "D" => display); let filename = format!("/tmp/.X{}-lock", display); let lockfile = ::std::fs::OpenOptions::new() .write(true) @@ -47,11 +46,13 @@ impl X11Lock { let _ = ::std::fs::remove_file(&filename); return Err(()); } else { + debug!(log, "X11 lock aquired"; "D" => display); // we got the lockfile and wrote our pid to it, all is good - return Ok(X11Lock { display }); + return Ok(X11Lock { display, log }); } } Err(_) => { + debug!(log, "Failed to acquire lock"; "D" => display); // we could not open the file, now we try to read it // and if it contains the pid of a process that no longer // exist (so if a previous x server claimed it and did not @@ -69,8 +70,13 @@ impl X11Lock { if let Err(NixError::Sys(Errno::ESRCH)) = ::nix::sys::signal::kill(pid, None) { // no process whose pid equals the contents of the lockfile exists // remove the lockfile and try grabbing it again - let _ = ::std::fs::remove_file(filename); - return X11Lock::grab(display); + if let Ok(()) = ::std::fs::remove_file(filename) { + debug!(log, "Lock was blocked by a defunct X11 server, trying again"; "D" => display); + return X11Lock::grab(display, log); + } else { + // we could not remove the lockfile, abort + return Err(()); + } } // if we reach here, this lockfile exists and is probably in use, give up return Err(()); @@ -85,15 +91,20 @@ impl X11Lock { impl Drop for X11Lock { fn drop(&mut self) { + info!(self.log, "Cleaning up X11 lock."); // Cleanup all the X11 files - let _ = ::std::fs::remove_file(format!("/tmp/.X11-unix/X{}", self.display)); - let _ = ::std::fs::remove_file(format!("/tmp/.X{}-lock", self.display)); + if let Err(e) = ::std::fs::remove_file(format!("/tmp/.X11-unix/X{}", self.display)) { + warn!(self.log, "Failed to remove X11 socket"; "error" => format!("{:?}", e)); + } + if let Err(e) = ::std::fs::remove_file(format!("/tmp/.X{}-lock", self.display)) { + warn!(self.log, "Failed to remove X11 lockfile"; "error" => format!("{:?}", e)); + } } } /// Open the two unix sockets an X server listens on /// -/// SHould only be done after the associated lockfile is aquired! +/// Should only be done after the associated lockfile is aquired! fn open_x11_sockets_for_display(display: u32) -> NixResult<[UnixStream; 2]> { let path = format!("/tmp/.X11-unix/X{}", display); // We know this path is not to long, these unwrap cannot fail diff --git a/src/xwayland/xserver.rs b/src/xwayland/xserver.rs index e8afe5b..3240ff8 100644 --- a/src/xwayland/xserver.rs +++ b/src/xwayland/xserver.rs @@ -66,12 +66,22 @@ pub trait XWindowManager { impl XWayland { /// Start the XWayland server - pub fn init(wm: WM, token: LoopToken, display: Rc>) -> Result, ()> { + pub fn init( + wm: WM, + token: LoopToken, + display: Rc>, + logger: L, + ) -> Result, ()> + where + L: Into>, + { + let log = ::slog_or_stdlog(logger); let inner = Rc::new(RefCell::new(Inner { wm, token, wayland_display: display, instance: None, + log: log.new(o!("smithay_module" => "XWayland")), })); launch(&inner)?; Ok(XWayland { inner }) @@ -99,6 +109,7 @@ struct Inner { token: LoopToken, wayland_display: Rc>, instance: Option, + log: ::slog::Logger, } // Launch an XWayland server @@ -110,10 +121,12 @@ fn launch(inner: &Rc>>) -> Resul return Ok(()); } + info!(guard.log, "Starting XWayland"); + let (x_wm_x11, x_wm_me) = UnixStream::pair().map_err(|_| ())?; let (wl_x11, wl_me) = UnixStream::pair().map_err(|_| ())?; - let (lock, x_fds) = prepare_x11_sockets()?; + let (lock, x_fds) = prepare_x11_sockets(guard.log.clone())?; // we have now created all the required sockets @@ -174,20 +187,20 @@ fn launch(inner: &Rc>>) -> Resul Ok(x) => match x {}, Err(e) => { // well, what can we do ? - eprintln!("[smithay] exec XWayland failed: {:?}", e); + error!(guard.log, "exec XWayland failed"; "err" => format!("{:?}", e)); unsafe { ::nix::libc::exit(1) }; } } } Err(e) => { // well, what can we do ? - eprintln!("[smithay] XWayland second fork failed: {:?}", e); + error!(guard.log, "XWayland second fork failed"; "err" => format!("{:?}", e)); unsafe { ::nix::libc::exit(1) }; } } } Err(e) => { - eprintln!("[smithay] XWayland first fork failed: {:?}", e); + error!(guard.log, "XWayland first fork failed"; "err" => format!("{:?}", e)); return Err(()); } }; @@ -209,6 +222,7 @@ impl Inner { fn shutdown(&mut self) { // don't do anything if not running if let Some(mut instance) = self.instance.take() { + info!(self.log, "Shutting down XWayland."); self.wm.xwayland_exited(); // kill the client instance.wayland_client.kill(); @@ -238,7 +252,13 @@ fn client_destroy(data: *mut ()) { // restart it, unless we really just started it, if it crashes right // at startup there is no point if started_at.map(|t| t.elapsed().as_secs()).unwrap_or(10) > 5 { + warn!(inner.borrow().log, "XWayland crashed, restarting."); let _ = launch(&inner); + } else { + warn!( + inner.borrow().log, + "XWayland crashed less than 5 seconds after its startup, not restarting." + ); } } @@ -253,7 +273,7 @@ fn xwayland_ready(inner: &Rc>>) { let pid = instance.child_pid.unwrap(); // find out if the launch was a success by waiting on the intermediate child - let mut success: bool; + let success: bool; loop { match wait::waitpid(pid, None) { Ok(wait::WaitStatus::Exited(_, 0)) => { @@ -275,6 +295,7 @@ fn xwayland_ready(inner: &Rc>>) { 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(), @@ -282,6 +303,11 @@ fn xwayland_ready(inner: &Rc>>) { // setup the environemnt ::std::env::set_var("DISPLAY", format!(":{}", instance.display_lock.display())); + } else { + error!( + inner.log, + "XWayland crashed at startup, will not try to restart it." + ); } // in all cases, cleanup From 89cecd6fee9133a2722a5c69d58b061970a7ca59 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 4 May 2018 10:57:18 +0200 Subject: [PATCH 5/5] travis: add xwayland feature --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7d93485..cd24a90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,6 +42,7 @@ env: - FEATURES="backend_session_udev" - FEATURES="backend_session_logind" - FEATURES="renderer_glium" + - FEATURES="xwayland" # test default features - FEATURES="default" # test all features simultaneously