use { crate::{ utils::{ errorfmt::ErrorFmt, oserror::{OsError, OsErrorExt, OsErrorExt2}, }, xwayland::XWaylandError, }, std::{ io::{Read, Write}, rc::Rc, }, uapi::{Errno, OwnedFd, Ustring, c, format_ustr}, }; const SOCK_DIR: &str = "/tmp/.X11-unix"; pub struct XSocket { pub id: u32, pub path: Ustring, pub lock_path: Ustring, } impl Drop for XSocket { fn drop(&mut self) { let _ = uapi::unlink(&self.path); let _ = uapi::unlink(&self.lock_path); } } fn bind_socket(fd: &Rc, id: u32) -> Result<(XSocket, Rc), XWaylandError> { let path = format_ustr!("{}/X{}", SOCK_DIR, id); let lock_path = format_ustr!("/tmp/.X{}-lock", id); let mut lock_fd = 'open_lock_file: { for i in 0..2 { if let Ok(fd) = uapi::open( &*lock_path, c::O_CREAT | c::O_CLOEXEC | c::O_WRONLY | c::O_EXCL, 0o444, ) { break 'open_lock_file fd; } if i == 1 { return Err(XWaylandError::AlreadyInUse); } let mut fd = uapi::open(&*lock_path, c::O_CLOEXEC | c::O_RDONLY, 0) .map_os_err(XWaylandError::ReadLockFile)?; let mut pid = String::new(); if let Err(e) = fd.read_to_string(&mut pid) { return Err(XWaylandError::ReadLockFile(e.into())); } let pid = match pid.trim().parse() { Ok(p) => p, Err(e) => return Err(XWaylandError::NotALockFile(e)), }; match uapi::kill(pid, 0) { Err(Errno(c::ESRCH)) => { let _ = uapi::unlink(&lock_path); } _ => return Err(XWaylandError::AlreadyInUse), } } return Err(XWaylandError::AlreadyInUse); }; let _ = uapi::unlink(&path); let mut addr: c::sockaddr_un = uapi::pod_zeroed(); addr.sun_family = c::AF_UNIX as _; let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]); sun_path[..path.len()].copy_from_slice(path.as_bytes()); sun_path[path.len()] = 0; uapi::bind(fd.raw(), &addr).map_os_err(XWaylandError::BindFailed)?; let s = format!("{:10}\n", uapi::getpid()); if let Err(e) = lock_fd.write_all(s.as_bytes()) { return Err(XWaylandError::WriteLockFile(e.into())); } let xsocket = XSocket { id, path, lock_path, }; Ok((xsocket, fd.clone())) } pub(super) fn allocate_socket() -> Result<(XSocket, Rc), XWaylandError> { match uapi::stat(SOCK_DIR).to_os_error() { Err(OsError(c::ENOENT)) => return Err(XWaylandError::MissingSocketDir), Err(e) => return Err(XWaylandError::StatSocketDir(e)), Ok(s) if s.st_mode & c::S_IFMT != c::S_IFDIR => return Err(XWaylandError::NotASocketDir), _ => { if uapi::access(SOCK_DIR, c::W_OK).is_err() { return Err(XWaylandError::SocketDirNotWritable); } } } let fd = uapi::socket(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0) .map(Rc::new) .map_os_err(XWaylandError::SocketFailed)?; for i in 500..1500 { match bind_socket(&fd, i) { Ok(s) => return Ok(s), Err(e) => { log::warn!("Cannot use the :{} display: {}", i, ErrorFmt(e)); } } } Err(XWaylandError::AddressesInUse) }