xwayland: Use Rust's UnixStream instead of RawFd

THis allows us to take advantage of RAII for cleanup, among others.
This commit is contained in:
Victor Berger 2018-05-01 11:56:32 +02:00
parent d2cbadc670
commit 5ae34d2613
2 changed files with 44 additions and 97 deletions

View File

@ -1,21 +1,13 @@
use std::io::{Read, Write}; 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::{Error as NixError, Result as NixResult};
use nix::errno::Errno; use nix::errno::Errno;
use nix::sys::socket; 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 /// 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 { for d in 0..33 {
// if fails, try the next one // if fails, try the next one
if let Err(()) = grab_lockfile(d) { 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 /// 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<[RawFd; 2]> { fn open_x11_sockets_for_display(display: u32) -> NixResult<[UnixStream; 2]> {
let fs_socket = open_socket( let path = format!("/tmp/.X11-unix/X{}", display);
socket::UnixAddr::new(format!("/tmp/.X11-unix/X{}", display).as_bytes()).unwrap(), // We know this path is not to long, this unwrap cannot fail // We know this path is not to long, these unwrap cannot fail
)?; let fs_addr = socket::UnixAddr::new(path.as_bytes()).unwrap();
let ret = open_socket( let abs_addr = socket::UnixAddr::new_abstract(path.as_bytes()).unwrap();
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 let fs_socket = open_socket(fs_addr)?;
); let abstract_socket = open_socket(abs_addr)?;
match ret { Ok([fs_socket, abstract_socket])
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 /// Open an unix socket for listening and bind it to given path
fn open_socket(addr: socket::UnixAddr) -> NixResult<RawFd> { fn open_socket(addr: socket::UnixAddr) -> NixResult<UnixStream> {
// create an unix stream socket // create an unix stream socket
let fd = socket::socket( let fd = socket::socket(
socket::AddressFamily::Unix, socket::AddressFamily::Unix,
@ -132,5 +117,5 @@ fn open_socket(addr: socket::UnixAddr) -> NixResult<RawFd> {
let _ = ::nix::unistd::close(fd); let _ = ::nix::unistd::close(fd);
return Err(e); return Err(e);
} }
Ok(fd) Ok(unsafe { FromRawFd::from_raw_fd(fd) })
} }

View File

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