xwayland: take advantage of RAII for X11 lockfile
This commit is contained in:
parent
5ae34d2613
commit
2d8653d9d7
|
@ -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<X11Lock, ()> {
|
||||
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::<i32>()
|
||||
.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::<i32>()
|
||||
.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
|
||||
|
|
|
@ -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<WM: XWindowManager> {
|
||||
|
@ -85,7 +85,7 @@ impl<WM: XWindowManager> Drop for XWayland<WM> {
|
|||
}
|
||||
|
||||
struct XWaylandInstance {
|
||||
display: u32,
|
||||
display_lock: X11Lock,
|
||||
wayland_client: Client,
|
||||
sigusr1_handler: Option<Source<SignalEvent>>,
|
||||
wm_fd: Option<UnixStream>,
|
||||
|
@ -113,7 +113,7 @@ fn launch<WM: XWindowManager + 'static>(inner: &Rc<RefCell<Inner<WM>>>) -> 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<WM: XWindowManager + 'static>(inner: &Rc<RefCell<Inner<WM>>>) -> Resul
|
|||
let creation_time = ::std::time::Instant::now();
|
||||
|
||||
// create the wayland client for XWayland
|
||||
let client = unsafe { guard.wayland_display.borrow_mut().create_client(wl_me.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::<WM>);
|
||||
|
||||
// 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<WM: XWindowManager + 'static>(inner: &Rc<RefCell<Inner<WM>>>) -> 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<WM: XWindowManager + 'static>(inner: &Rc<RefCell<Inner<WM>>>) -> 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<WM: XWindowManager> Inner<WM> {
|
|||
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<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
|
|||
// 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
|
||||
|
|
Loading…
Reference in New Issue