Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1,71 @@
//! Debugging helpers to handle `WAYLAND_DEBUG` env variable.
use std::time::{SystemTime, UNIX_EPOCH};
use crate::wire::Argument;
/// Print the dispatched message to stderr in a following format:
///
/// [timestamp] <- interface@id.msg_name(args)
pub fn print_dispatched_message(interface: &str, id: u32, msg_name: &str, args: &[Argument]) {
// Add timestamp to output.
print_timestamp();
eprint!(" <- {}@{}.{}", interface, id, msg_name);
print_args(args);
// Add a new line.
eprintln!();
}
/// Print the send message to stderr in a following format:
///
/// [timestamp] -> interface@id.msg_name(args)
///
/// If `is_alive` is `false` the `[ZOMBIE]` is added after `id`.
pub fn print_send_message(
interface: &str,
id: u32,
is_alive: bool,
msg_name: &str,
args: &[Argument],
) {
// Add timestamp to output.
print_timestamp();
eprint!(" -> {}@{}{}.{}", interface, id, if is_alive { "" } else { "[ZOMBIE]" }, msg_name);
print_args(args);
// Add a new line.
eprintln!();
}
/// Print arguments with opening/closing bracket.
fn print_args(args: &[Argument]) {
let num_args = args.len();
eprint!("(");
if num_args > 0 {
// Explicitly handle first argument to handle one arg functions nicely.
eprint!("{}", args[0]);
// Handle the rest.
for arg in args.iter().take(num_args).skip(1) {
eprint!(", {}", arg);
}
}
eprint!(")")
}
/// Print timestamp in seconds.microseconds format.
fn print_timestamp() {
if let Ok(timestamp) = SystemTime::now().duration_since(UNIX_EPOCH) {
let sc = timestamp.as_secs();
let ms = timestamp.subsec_micros();
eprint!("[{}.{:06}]", sc, ms);
}
}

View file

@ -0,0 +1,109 @@
//! Filter
use std::{cell::RefCell, collections::VecDeque, rc::Rc};
/// Holder of global dispatch-related data
///
/// This struct serves as a dynamic container for the dispatch-time
/// global data that you gave to the dispatch method, and is given as
/// input to all your callbacks. It allows you to share global state
/// between your filters.
///
/// The main method of interest is the `get` method, which allows you to
/// access a `&mut _` reference to the global data itself. The other methods
/// are mostly used internally by the crate.
pub struct DispatchData<'a> {
data: &'a mut dyn std::any::Any,
}
impl<'a> std::fmt::Debug for DispatchData<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("DispatchData { ... }")
}
}
impl<'a> DispatchData<'a> {
/// Access the dispatch data knowing its type
///
/// Will return `None` if the provided type is not the correct
/// inner type.
pub fn get<T: std::any::Any>(&mut self) -> Option<&mut T> {
self.data.downcast_mut()
}
/// Wrap a mutable reference
///
/// This creates a new `DispatchData` from a mutable reference
pub fn wrap<T: std::any::Any>(data: &'a mut T) -> DispatchData<'a> {
DispatchData { data }
}
/// Reborrows this `DispatchData` to create a new one with the same content
///
/// This is a quick and cheap way to propagate the `DispatchData` down a
/// callback stack by value. It is basically a noop only there to ease
/// work with the borrow checker.
pub fn reborrow(&mut self) -> DispatchData {
DispatchData { data: &mut *self.data }
}
}
struct Inner<E, F: ?Sized> {
pending: RefCell<VecDeque<E>>,
cb: RefCell<F>,
}
type DynInner<E> = Inner<E, dyn FnMut(E, &Filter<E>, DispatchData<'_>)>;
/// An event filter
///
/// Can be used in wayland-client and wayland-server to aggregate
/// messages from different objects into the same closure.
///
/// You need to provide it a closure of type `FnMut(E, &Filter<E>)`,
/// which will be called any time a message is sent to the filter
/// via the `send(..)` method. Your closure also receives a handle
/// to the filter as argument, so that you can use it from within
/// the callback (to assign new wayland objects to this filter for
/// example).
///
/// The `Filter` can be cloned, and all clones send messages to the
/// same closure. However it is not threadsafe.
pub struct Filter<E> {
inner: Rc<DynInner<E>>,
}
impl<E: std::fmt::Debug> std::fmt::Debug for Filter<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Filter").field("pending", &self.inner.pending).finish()
}
}
impl<E> Clone for Filter<E> {
fn clone(&self) -> Filter<E> {
Filter { inner: self.inner.clone() }
}
}
impl<E> Filter<E> {
/// Create a new filter from given closure
pub fn new<F: FnMut(E, &Filter<E>, DispatchData<'_>) + 'static>(f: F) -> Filter<E> {
Filter {
inner: Rc::new(Inner { pending: RefCell::new(VecDeque::new()), cb: RefCell::new(f) }),
}
}
/// Send a message to this filter
pub fn send(&self, evt: E, mut data: DispatchData) {
// gracefully handle reentrancy
if let Ok(mut guard) = self.inner.cb.try_borrow_mut() {
(&mut *guard)(evt, self, data.reborrow());
// process all events that might have been enqueued by the cb
while let Some(evt) = self.inner.pending.borrow_mut().pop_front() {
(&mut *guard)(evt, self, data.reborrow());
}
} else {
self.inner.pending.borrow_mut().push_back(evt);
}
}
}

View file

@ -0,0 +1,228 @@
//! Common definitions for wayland
//!
//! This crate hosts common type and traits used to represent wayland messages
//! and routines in the `wayland-client` and `wayland-server` crates.
//!
//! This notably includes the `Interface` trait, which can exhaustively describe
//! any wayland interface. Its implementations are intended to be generated by the
//! `wayland-scanner` crate.
//!
//! The principal user-facing definition provided by this crate is the `Implementation`
//! trait, which as a user of `wayland-client` or `wayland-server` you will be using
//! to define objects able to handle the messages your program receives. Note that
//! this trait is auto-implemented for closures with appropriate signature, for
//! convenience.
#![warn(missing_docs, missing_debug_implementations)]
#[macro_use]
extern crate nix;
use std::os::raw::c_void;
use wayland_sys::common as syscom;
pub mod debug;
pub mod filter;
pub mod map;
pub mod socket;
pub mod user_data;
pub mod wire;
pub use smallvec::smallvec;
/// A group of messages
///
/// This represents a group of message that can be serialized on the protocol wire.
/// Typically the set of events or requests of a single interface.
///
/// Implementations of this trait are supposed to be
/// generated using the `wayland-scanner` crate.
pub trait MessageGroup: Sized {
/// Wire representation of this MessageGroup
const MESSAGES: &'static [wire::MessageDesc];
/// The wrapper type for ObjectMap allowing the mapping of Object and
/// NewId arguments to the object map during parsing.
type Map;
/// The opcode of this message
fn opcode(&self) -> u16;
/// Whether this message is a destructor
///
/// If it is, once send or receive the associated object cannot be used any more.
fn is_destructor(&self) -> bool;
/// The minimal object version for which this message exists
fn since(&self) -> u32;
/// Retrieve the child `Object` associated with this message if any
fn child<Meta: self::map::ObjectMetadata>(
opcode: u16,
version: u32,
meta: &Meta,
) -> Option<crate::map::Object<Meta>>;
/// Construct a message from its raw representation
// -- The lint is allowed because fixing it would be a breaking change --
#[allow(clippy::result_unit_err)]
fn from_raw(msg: wire::Message, map: &mut Self::Map) -> Result<Self, ()>;
/// Turn this message into its raw representation
fn into_raw(self, send_id: u32) -> wire::Message;
/// Construct a message of this group from its C representation
///
/// # Safety
///
/// The pointers provided to this function must all be valid pointers from
/// `libwayland-client`
// -- The lint is allowed because fixing it would be a breaking change --
#[allow(clippy::result_unit_err)]
unsafe fn from_raw_c(
obj: *mut c_void,
opcode: u32,
args: *const syscom::wl_argument,
) -> Result<Self, ()>;
/// Build a C representation of this message
///
/// It can only be accessed from the provided closure, and this consumes
/// the message.
// -- The lint is allowed because fixing it would be a breaking change --
#[allow(clippy::wrong_self_convention)]
fn as_raw_c_in<F, T>(self, f: F) -> T
where
F: FnOnce(u32, &mut [syscom::wl_argument]) -> T;
}
/// The description of a wayland interface
///
/// Implementations of this trait are supposed to be
/// generated using the `wayland-scanner` crate.
pub trait Interface: 'static {
/// Set of requests associated to this interface
///
/// Requests are messages from the client to the server
type Request: MessageGroup + 'static;
/// Set of events associated to this interface
///
/// Events are messages from the server to the client
type Event: MessageGroup + 'static;
/// Name of this interface
const NAME: &'static str;
/// Maximum supported version of this interface
///
/// This is the maximum version supported by the protocol specification currently
/// used by this library, and should not be used as-is in your code, as a version
/// change can subtly change the behavior of some objects.
///
/// Server are supposed to be able to handle all versions from 1 to the one they
/// advertise through the registry, and clients can choose any version among the
/// ones the server supports.
const VERSION: u32;
/// Pointer to the C representation of this interface
fn c_interface() -> *const syscom::wl_interface;
}
/// An empty enum representing a MessageGroup with no messages
#[derive(Debug)]
pub enum NoMessage {}
#[cfg(not(tarpaulin_include))]
impl MessageGroup for NoMessage {
const MESSAGES: &'static [wire::MessageDesc] = &[];
type Map = ();
fn is_destructor(&self) -> bool {
match *self {}
}
fn opcode(&self) -> u16 {
match *self {}
}
fn since(&self) -> u32 {
match *self {}
}
fn child<M: self::map::ObjectMetadata>(_: u16, _: u32, _: &M) -> Option<crate::map::Object<M>> {
None
}
fn from_raw(_: wire::Message, _: &mut ()) -> Result<Self, ()> {
Err(())
}
fn into_raw(self, _: u32) -> wire::Message {
match self {}
}
unsafe fn from_raw_c(
_obj: *mut c_void,
_opcode: u32,
_args: *const syscom::wl_argument,
) -> Result<Self, ()> {
Err(())
}
fn as_raw_c_in<F, T>(self, _f: F) -> T
where
F: FnOnce(u32, &mut [syscom::wl_argument]) -> T,
{
match self {}
}
}
/// Stores a value in a threadafe container that
/// only lets you access it from its owning thread
///
/// If the ThreadGuard is dropped from the wrong thread,
/// the underlying value will be leaked.
#[derive(Debug)]
pub struct ThreadGuard<T: ?Sized> {
thread: std::thread::ThreadId,
val: std::mem::ManuallyDrop<T>,
}
impl<T> ThreadGuard<T> {
/// Create a new ThreadGuard wrapper
pub fn new(val: T) -> ThreadGuard<T> {
ThreadGuard { val: std::mem::ManuallyDrop::new(val), thread: std::thread::current().id() }
}
}
impl<T: ?Sized> ThreadGuard<T> {
/// Access the underlying value
///
/// Panics if done on the wrong thread
pub fn get(&self) -> &T {
self.try_get().expect("Attempted to access a ThreadGuard contents from the wrong thread.")
}
/// Mutably access the underlying value
///
/// Panics if done on the wrong thread
pub fn get_mut(&mut self) -> &mut T {
self.try_get_mut()
.expect("Attempted to access a ThreadGuard contents from the wrong thread.")
}
/// Try to access the underlying value
///
/// Returns `None` if done on the wrong thread
pub fn try_get(&self) -> Option<&T> {
if self.thread == ::std::thread::current().id() {
Some(&self.val)
} else {
None
}
}
/// Try to mutably access the underlying value
///
/// Returns `None` if done on the wrong thread
pub fn try_get_mut(&mut self) -> Option<&mut T> {
if self.thread == ::std::thread::current().id() {
Some(&mut self.val)
} else {
None
}
}
}
impl<T: ?Sized> Drop for ThreadGuard<T> {
fn drop(&mut self) {
// We can only actually perform the drop if we are on the right thread
// otherwise it may be racy, so we just leak the value
if self.thread == ::std::thread::current().id() {
unsafe { std::mem::ManuallyDrop::drop(&mut self.val) }
}
}
}
unsafe impl<T: ?Sized> Send for ThreadGuard<T> {}
unsafe impl<T: ?Sized> Sync for ThreadGuard<T> {}

View file

@ -0,0 +1,252 @@
//! Wayland objects map
use crate::{Interface, MessageGroup, NoMessage};
use std::cmp::Ordering;
/// Limit separating server-created from client-created objects IDs in the namespace
pub const SERVER_ID_LIMIT: u32 = 0xFF00_0000;
/// A trait representing the metadata a wayland implementation
/// may attach to an object.
pub trait ObjectMetadata: Clone {
/// Create the metadata for a child object
///
/// Mostly needed for client side, to propagate the event queues
fn child(&self) -> Self;
}
impl ObjectMetadata for () {
fn child(&self) {}
}
/// The representation of a protocol object
#[derive(Clone)]
pub struct Object<Meta: ObjectMetadata> {
/// Interface name of this object
pub interface: &'static str,
/// Version of this object
pub version: u32,
/// Description of the requests of this object
pub requests: &'static [crate::wire::MessageDesc],
/// Description of the events of this object
pub events: &'static [crate::wire::MessageDesc],
/// Metadata associated to this object (ex: its event queue client side)
pub meta: Meta,
/// A function which, from an opcode, a version, and the Meta, creates a child
/// object associated with this event if any
pub childs_from_events: fn(u16, u32, &Meta) -> Option<Object<Meta>>,
/// A function which, from an opcode, a version, and the Meta, creates a child
/// object associated with this request if any
pub childs_from_requests: fn(u16, u32, &Meta) -> Option<Object<Meta>>,
}
impl<Meta: ObjectMetadata + std::fmt::Debug> std::fmt::Debug for Object<Meta> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Object")
.field("interface", &self.interface)
.field("version", &self.version)
.field("requests", &self.requests)
.field("events", &self.events)
.field("meta", &self.meta)
.finish()
}
}
impl<Meta: ObjectMetadata> Object<Meta> {
/// Create an Object corresponding to given interface and version
pub fn from_interface<I: Interface>(version: u32, meta: Meta) -> Object<Meta> {
Object {
interface: I::NAME,
version,
requests: I::Request::MESSAGES,
events: I::Event::MESSAGES,
meta,
childs_from_events: childs_from::<I::Event, Meta>,
childs_from_requests: childs_from::<I::Request, Meta>,
}
}
/// Create an optional `Object` corresponding to the possible `new_id` associated
/// with given event opcode
pub fn event_child(&self, opcode: u16) -> Option<Object<Meta>> {
(self.childs_from_events)(opcode, self.version, &self.meta)
}
/// Create an optional `Object` corresponding to the possible `new_id` associated
/// with given request opcode
pub fn request_child(&self, opcode: u16) -> Option<Object<Meta>> {
(self.childs_from_requests)(opcode, self.version, &self.meta)
}
/// Check whether this object is of given interface
pub fn is_interface<I: Interface>(&self) -> bool {
// TODO: we might want to be more robust than that
self.interface == I::NAME
}
/// Create a placeholder object that will be filled-in by the message logic
pub fn placeholder(meta: Meta) -> Object<Meta> {
Object {
interface: "",
version: 0,
requests: &[],
events: &[],
meta,
childs_from_events: childs_from::<NoMessage, Meta>,
childs_from_requests: childs_from::<NoMessage, Meta>,
}
}
}
fn childs_from<M: MessageGroup, Meta: ObjectMetadata>(
opcode: u16,
version: u32,
meta: &Meta,
) -> Option<Object<Meta>> {
M::child(opcode, version, meta)
}
/// A holder for the object store of a connection
///
/// Keeps track of which object id is associated to which
/// interface object, and which is currently unused.
#[derive(Default, Debug)]
pub struct ObjectMap<Meta: ObjectMetadata> {
client_objects: Vec<Option<Object<Meta>>>,
server_objects: Vec<Option<Object<Meta>>>,
}
impl<Meta: ObjectMetadata> ObjectMap<Meta> {
/// Create a new empty object map
pub fn new() -> ObjectMap<Meta> {
ObjectMap { client_objects: Vec::new(), server_objects: Vec::new() }
}
/// Find an object in the store
pub fn find(&self, id: u32) -> Option<Object<Meta>> {
if id == 0 {
None
} else if id >= SERVER_ID_LIMIT {
self.server_objects.get((id - SERVER_ID_LIMIT) as usize).and_then(Clone::clone)
} else {
self.client_objects.get((id - 1) as usize).and_then(Clone::clone)
}
}
/// Remove an object from the store
///
/// Does nothing if the object didn't previously exists
pub fn remove(&mut self, id: u32) {
if id == 0 {
// nothing
} else if id >= SERVER_ID_LIMIT {
if let Some(place) = self.server_objects.get_mut((id - SERVER_ID_LIMIT) as usize) {
*place = None;
}
} else if let Some(place) = self.client_objects.get_mut((id - 1) as usize) {
*place = None;
}
}
/// Insert given object for given id
///
/// Can fail if the requested id is not the next free id of this store.
/// (In which case this is a protocol error)
// -- The lint is allowed because fixing it would be a breaking change --
#[allow(clippy::result_unit_err)]
pub fn insert_at(&mut self, id: u32, object: Object<Meta>) -> Result<(), ()> {
if id == 0 {
Err(())
} else if id >= SERVER_ID_LIMIT {
insert_in_at(&mut self.server_objects, (id - SERVER_ID_LIMIT) as usize, object)
} else {
insert_in_at(&mut self.client_objects, (id - 1) as usize, object)
}
}
/// Allocate a new id for an object in the client namespace
pub fn client_insert_new(&mut self, object: Object<Meta>) -> u32 {
insert_in(&mut self.client_objects, object) + 1
}
/// Allocate a new id for an object in the server namespace
pub fn server_insert_new(&mut self, object: Object<Meta>) -> u32 {
insert_in(&mut self.server_objects, object) + SERVER_ID_LIMIT
}
/// Mutably access an object of the map
// -- The lint is allowed because fixing it would be a breaking change --
#[allow(clippy::result_unit_err)]
pub fn with<T, F: FnOnce(&mut Object<Meta>) -> T>(&mut self, id: u32, f: F) -> Result<T, ()> {
if id == 0 {
Err(())
} else if id >= SERVER_ID_LIMIT {
if let Some(&mut Some(ref mut obj)) =
self.server_objects.get_mut((id - SERVER_ID_LIMIT) as usize)
{
Ok(f(obj))
} else {
Err(())
}
} else if let Some(&mut Some(ref mut obj)) = self.client_objects.get_mut((id - 1) as usize)
{
Ok(f(obj))
} else {
Err(())
}
}
/// Mutably access all objects of the map in sequence
pub fn with_all<F: FnMut(u32, &mut Object<Meta>)>(&mut self, mut f: F) {
for (id, place) in self.client_objects.iter_mut().enumerate() {
if let Some(ref mut obj) = *place {
f(id as u32 + 1, obj);
}
}
for (id, place) in self.server_objects.iter_mut().enumerate() {
if let Some(ref mut obj) = *place {
f(id as u32 + SERVER_ID_LIMIT, obj);
}
}
}
}
// insert a new object in a store at the first free place
fn insert_in<Meta: ObjectMetadata>(
store: &mut Vec<Option<Object<Meta>>>,
object: Object<Meta>,
) -> u32 {
match store.iter().position(Option::is_none) {
Some(id) => {
store[id] = Some(object);
id as u32
}
None => {
store.push(Some(object));
(store.len() - 1) as u32
}
}
}
// insert an object at a given place in a store
fn insert_in_at<Meta: ObjectMetadata>(
store: &mut Vec<Option<Object<Meta>>>,
id: usize,
object: Object<Meta>,
) -> Result<(), ()> {
match id.cmp(&store.len()) {
Ordering::Greater => Err(()),
Ordering::Equal => {
store.push(Some(object));
Ok(())
}
Ordering::Less => {
let previous = &mut store[id];
if !previous.is_none() {
return Err(());
}
*previous = Some(object);
Ok(())
}
}
}

View file

@ -0,0 +1,681 @@
//! Wayland socket manipulation
use std::io::{IoSlice, IoSliceMut};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use nix::{sys::socket, Result as NixResult};
use crate::wire::{ArgumentType, Message, MessageParseError, MessageWriteError};
/// Maximum number of FD that can be sent in a single socket message
pub const MAX_FDS_OUT: usize = 28;
/// Maximum number of bytes that can be sent in a single socket message
pub const MAX_BYTES_OUT: usize = 4096;
/*
* Socket
*/
/// A wayland socket
#[derive(Debug)]
pub struct Socket {
fd: RawFd,
}
impl Socket {
/// Send a single message to the socket
///
/// A single socket message can contain several wayland messages
///
/// The `fds` slice should not be longer than `MAX_FDS_OUT`, and the `bytes`
/// slice should not be longer than `MAX_BYTES_OUT` otherwise the receiving
/// end may lose some data.
pub fn send_msg(&self, bytes: &[u8], fds: &[RawFd]) -> NixResult<()> {
let flags = socket::MsgFlags::MSG_DONTWAIT | socket::MsgFlags::MSG_NOSIGNAL;
let iov = [IoSlice::new(bytes)];
if !fds.is_empty() {
let cmsgs = [socket::ControlMessage::ScmRights(fds)];
socket::sendmsg::<()>(self.fd, &iov, &cmsgs, flags, None)?;
} else {
socket::sendmsg::<()>(self.fd, &iov, &[], flags, None)?;
};
Ok(())
}
/// Receive a single message from the socket
///
/// Return the number of bytes received and the number of Fds received.
///
/// Errors with `WouldBlock` is no message is available.
///
/// A single socket message can contain several wayland messages.
///
/// The `buffer` slice should be at least `MAX_BYTES_OUT` long and the `fds`
/// slice `MAX_FDS_OUT` long, otherwise some data of the received message may
/// be lost.
pub fn rcv_msg(&self, buffer: &mut [u8], fds: &mut [RawFd]) -> NixResult<(usize, usize)> {
let mut cmsg = cmsg_space!([RawFd; MAX_FDS_OUT]);
let mut iov = [IoSliceMut::new(buffer)];
let msg = socket::recvmsg::<()>(
self.fd,
&mut iov[..],
Some(&mut cmsg),
socket::MsgFlags::MSG_DONTWAIT
| socket::MsgFlags::MSG_CMSG_CLOEXEC
| socket::MsgFlags::MSG_NOSIGNAL,
)?;
let mut fd_count = 0;
let received_fds = msg.cmsgs().flat_map(|cmsg| match cmsg {
socket::ControlMessageOwned::ScmRights(s) => s,
_ => Vec::new(),
});
for (fd, place) in received_fds.zip(fds.iter_mut()) {
fd_count += 1;
*place = fd;
}
Ok((msg.bytes, fd_count))
}
/// Retrieve the current value of the requested [socket::GetSockOpt]
pub fn opt<O: socket::GetSockOpt>(&self, opt: O) -> NixResult<O::Val> {
socket::getsockopt(self.fd, opt)
}
}
impl FromRawFd for Socket {
unsafe fn from_raw_fd(fd: RawFd) -> Socket {
Socket { fd }
}
}
impl AsRawFd for Socket {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
impl IntoRawFd for Socket {
fn into_raw_fd(self) -> RawFd {
self.fd
}
}
impl Drop for Socket {
fn drop(&mut self) {
let _ = ::nix::unistd::close(self.fd);
}
}
/*
* BufferedSocket
*/
/// An adapter around a raw Socket that directly handles buffering and
/// conversion from/to wayland messages
#[derive(Debug)]
pub struct BufferedSocket {
socket: Socket,
in_data: Buffer<u32>,
in_fds: Buffer<RawFd>,
out_data: Buffer<u32>,
out_fds: Buffer<RawFd>,
}
impl BufferedSocket {
/// Wrap a Socket into a Buffered Socket
pub fn new(socket: Socket) -> BufferedSocket {
BufferedSocket {
socket,
in_data: Buffer::new(2 * MAX_BYTES_OUT / 4), // Incoming buffers are twice as big in order to be
in_fds: Buffer::new(2 * MAX_FDS_OUT), // able to store leftover data if needed
out_data: Buffer::new(MAX_BYTES_OUT / 4),
out_fds: Buffer::new(MAX_FDS_OUT),
}
}
/// Get direct access to the underlying socket
pub fn get_socket(&mut self) -> &mut Socket {
&mut self.socket
}
/// Retrieve ownership of the underlying Socket
///
/// Any leftover content in the internal buffers will be lost
pub fn into_socket(self) -> Socket {
self.socket
}
/// Flush the contents of the outgoing buffer into the socket
pub fn flush(&mut self) -> NixResult<()> {
{
let words = self.out_data.get_contents();
if words.is_empty() {
return Ok(());
}
let bytes = unsafe {
::std::slice::from_raw_parts(words.as_ptr() as *const u8, words.len() * 4)
};
let fds = self.out_fds.get_contents();
self.socket.send_msg(bytes, fds)?;
for &fd in fds {
// once the fds are sent, we can close them
let _ = ::nix::unistd::close(fd);
}
}
self.out_data.clear();
self.out_fds.clear();
Ok(())
}
// internal method
//
// attempts to write a message in the internal out buffers,
// returns true if successful
//
// if false is returned, it means there is not enough space
// in the buffer
fn attempt_write_message(&mut self, msg: &Message) -> NixResult<bool> {
match msg.write_to_buffers(
self.out_data.get_writable_storage(),
self.out_fds.get_writable_storage(),
) {
Ok((bytes_out, fds_out)) => {
self.out_data.advance(bytes_out);
self.out_fds.advance(fds_out);
Ok(true)
}
Err(MessageWriteError::BufferTooSmall) => Ok(false),
Err(MessageWriteError::DupFdFailed(e)) => Err(e),
}
}
/// Write a message to the outgoing buffer
///
/// This method may flush the internal buffer if necessary (if it is full).
///
/// If the message is too big to fit in the buffer, the error `Error::Sys(E2BIG)`
/// will be returned.
pub fn write_message(&mut self, msg: &Message) -> NixResult<()> {
if !self.attempt_write_message(msg)? {
// the attempt failed, there is not enough space in the buffer
// we need to flush it
self.flush()?;
if !self.attempt_write_message(msg)? {
// If this fails again, this means the message is too big
// to be transmitted at all
return Err(::nix::Error::E2BIG);
}
}
Ok(())
}
/// Try to fill the incoming buffers of this socket, to prepare
/// a new round of parsing.
pub fn fill_incoming_buffers(&mut self) -> NixResult<()> {
// clear the buffers if they have no content
if !self.in_data.has_content() {
self.in_data.clear();
}
if !self.in_fds.has_content() {
self.in_fds.clear();
}
// receive a message
let (in_bytes, in_fds) = {
let words = self.in_data.get_writable_storage();
let bytes = unsafe {
::std::slice::from_raw_parts_mut(words.as_ptr() as *mut u8, words.len() * 4)
};
let fds = self.in_fds.get_writable_storage();
self.socket.rcv_msg(bytes, fds)?
};
if in_bytes == 0 {
// the other end of the socket was closed
return Err(::nix::Error::EPIPE);
}
// advance the storage
self.in_data.advance(in_bytes / 4 + if in_bytes % 4 > 0 { 1 } else { 0 });
self.in_fds.advance(in_fds);
Ok(())
}
/// Read and deserialize a single message from the incoming buffers socket
///
/// This method requires one closure that given an object id and an opcode,
/// must provide the signature of the associated request/event, in the form of
/// a `&'static [ArgumentType]`. If it returns `None`, meaning that
/// the couple object/opcode does not exist, an error will be returned.
///
/// There are 3 possibilities of return value:
///
/// - `Ok(Ok(msg))`: no error occurred, this is the message
/// - `Ok(Err(e))`: either a malformed message was encountered or we need more data,
/// in the latter case you need to try calling `fill_incoming_buffers()`.
/// - `Err(e)`: an I/O error occurred reading from the socked, details are in `e`
/// (this can be a "wouldblock" error, which just means that no message is available
/// to read)
pub fn read_one_message<F>(&mut self, mut signature: F) -> Result<Message, MessageParseError>
where
F: FnMut(u32, u16) -> Option<&'static [ArgumentType]>,
{
let (msg, read_data, read_fd) = {
let data = self.in_data.get_contents();
let fds = self.in_fds.get_contents();
if data.len() < 2 {
return Err(MessageParseError::MissingData);
}
let object_id = data[0];
let opcode = (data[1] & 0x0000_FFFF) as u16;
if let Some(sig) = signature(object_id, opcode) {
match Message::from_raw(data, sig, fds) {
Ok((msg, rest_data, rest_fds)) => {
(msg, data.len() - rest_data.len(), fds.len() - rest_fds.len())
}
// TODO: gracefully handle wayland messages split across unix messages ?
Err(e) => return Err(e),
}
} else {
// no signature found ?
return Err(MessageParseError::Malformed);
}
};
self.in_data.offset(read_data);
self.in_fds.offset(read_fd);
Ok(msg)
}
/// Read and deserialize messages from the socket
///
/// This method requires two closures:
///
/// - The first one, given an object id and an opcode, must provide
/// the signature of the associated request/event, in the form of
/// a `&'static [ArgumentType]`. If it returns `None`, meaning that
/// the couple object/opcode does not exist, the parsing will be
/// prematurely interrupted and this method will return a
/// `MessageParseError::Malformed` error.
/// - The second closure is charged to process the parsed message. If it
/// returns `false`, the iteration will be prematurely stopped.
///
/// In both cases of early stopping, the remaining unused data will be left
/// in the buffers, and will start to be processed at the next call of this
/// method.
///
/// There are 3 possibilities of return value:
///
/// - `Ok(Ok(n))`: no error occurred, `n` messages where processed
/// - `Ok(Err(MessageParseError::Malformed))`: a malformed message was encountered
/// (this is a protocol error and is supposed to be fatal to the connection).
/// - `Err(e)`: an I/O error occurred reading from the socked, details are in `e`
/// (this can be a "wouldblock" error, which just means that no message is available
/// to read)
pub fn read_messages<F1, F2>(
&mut self,
mut signature: F1,
mut callback: F2,
) -> NixResult<Result<usize, MessageParseError>>
where
F1: FnMut(u32, u16) -> Option<&'static [ArgumentType]>,
F2: FnMut(Message) -> bool,
{
// message parsing
let mut dispatched = 0;
loop {
let mut err = None;
// first parse any leftover messages
loop {
match self.read_one_message(&mut signature) {
Ok(msg) => {
let keep_going = callback(msg);
dispatched += 1;
if !keep_going {
break;
}
}
Err(e) => {
err = Some(e);
break;
}
}
}
// copy back any leftover content to the front of the buffer
self.in_data.move_to_front();
self.in_fds.move_to_front();
if let Some(MessageParseError::Malformed) = err {
// early stop here
return Ok(Err(MessageParseError::Malformed));
}
if err.is_none() && self.in_data.has_content() {
// we stopped reading without error while there is content? That means
// the user requested an early stopping
return Ok(Ok(dispatched));
}
// now, try to get more data
match self.fill_incoming_buffers() {
Ok(()) => (),
Err(e @ ::nix::Error::EAGAIN) => {
// stop looping, returning Ok() or EAGAIN depending on whether messages
// were dispatched
if dispatched == 0 {
return Err(e);
} else {
break;
}
}
Err(e) => return Err(e),
}
}
Ok(Ok(dispatched))
}
}
/*
* Buffer
*/
#[derive(Debug)]
struct Buffer<T: Copy> {
storage: Vec<T>,
occupied: usize,
offset: usize,
}
impl<T: Copy + Default> Buffer<T> {
fn new(size: usize) -> Buffer<T> {
Buffer { storage: vec![T::default(); size], occupied: 0, offset: 0 }
}
/// Check if this buffer has content to read
fn has_content(&self) -> bool {
self.occupied > self.offset
}
/// Advance the internal counter of occupied space
fn advance(&mut self, bytes: usize) {
self.occupied += bytes;
}
/// Advance the read offset of current occupied space
fn offset(&mut self, bytes: usize) {
self.offset += bytes;
}
/// Clears the contents of the buffer
///
/// This only sets the counter of occupied space back to zero,
/// allowing previous content to be overwritten.
fn clear(&mut self) {
self.occupied = 0;
self.offset = 0;
}
/// Get the current contents of the occupied space of the buffer
fn get_contents(&self) -> &[T] {
&self.storage[(self.offset)..(self.occupied)]
}
/// Get mutable access to the unoccupied space of the buffer
fn get_writable_storage(&mut self) -> &mut [T] {
&mut self.storage[(self.occupied)..]
}
/// Move the unread contents of the buffer to the front, to ensure
/// maximal write space availability
fn move_to_front(&mut self) {
unsafe {
::std::ptr::copy(
&self.storage[self.offset] as *const T,
&mut self.storage[0] as *mut T,
self.occupied - self.offset,
);
}
self.occupied -= self.offset;
self.offset = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wire::{Argument, ArgumentType, Message};
use std::ffi::CString;
use smallvec::smallvec;
fn same_file(a: RawFd, b: RawFd) -> bool {
let stat1 = ::nix::sys::stat::fstat(a).unwrap();
let stat2 = ::nix::sys::stat::fstat(b).unwrap();
stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino
}
// check if two messages are equal
//
// if arguments contain FDs, check that the fd point to
// the same file, rather than are the same number.
fn assert_eq_msgs(msg1: &Message, msg2: &Message) {
assert_eq!(msg1.sender_id, msg2.sender_id);
assert_eq!(msg1.opcode, msg2.opcode);
assert_eq!(msg1.args.len(), msg2.args.len());
for (arg1, arg2) in msg1.args.iter().zip(msg2.args.iter()) {
if let (&Argument::Fd(fd1), &Argument::Fd(fd2)) = (arg1, arg2) {
assert!(same_file(fd1, fd2));
} else {
assert_eq!(arg1, arg2);
}
}
}
#[test]
fn write_read_cycle() {
let msg = Message {
sender_id: 42,
opcode: 7,
args: smallvec![
Argument::Uint(3),
Argument::Fixed(-89),
Argument::Str(Box::new(CString::new(&b"I like trains!"[..]).unwrap())),
Argument::Array(vec![1, 2, 3, 4, 5, 6, 7, 8, 9].into()),
Argument::Object(88),
Argument::NewId(56),
Argument::Int(-25),
],
};
let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap();
let mut client = BufferedSocket::new(unsafe { Socket::from_raw_fd(client.into_raw_fd()) });
let mut server = BufferedSocket::new(unsafe { Socket::from_raw_fd(server.into_raw_fd()) });
client.write_message(&msg).unwrap();
client.flush().unwrap();
static SIGNATURE: &'static [ArgumentType] = &[
ArgumentType::Uint,
ArgumentType::Fixed,
ArgumentType::Str,
ArgumentType::Array,
ArgumentType::Object,
ArgumentType::NewId,
ArgumentType::Int,
];
let ret = server
.read_messages(
|sender_id, opcode| {
if sender_id == 42 && opcode == 7 {
Some(SIGNATURE)
} else {
None
}
},
|message| {
assert_eq_msgs(&message, &msg);
true
},
)
.unwrap()
.unwrap();
assert_eq!(ret, 1);
}
#[test]
fn write_read_cycle_fd() {
let msg = Message {
sender_id: 42,
opcode: 7,
args: smallvec![
Argument::Fd(1), // stdin
Argument::Fd(0), // stdout
],
};
let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap();
let mut client = BufferedSocket::new(unsafe { Socket::from_raw_fd(client.into_raw_fd()) });
let mut server = BufferedSocket::new(unsafe { Socket::from_raw_fd(server.into_raw_fd()) });
client.write_message(&msg).unwrap();
client.flush().unwrap();
static SIGNATURE: &'static [ArgumentType] = &[ArgumentType::Fd, ArgumentType::Fd];
let ret = server
.read_messages(
|sender_id, opcode| {
if sender_id == 42 && opcode == 7 {
Some(SIGNATURE)
} else {
None
}
},
|message| {
assert_eq_msgs(&message, &msg);
true
},
)
.unwrap()
.unwrap();
assert_eq!(ret, 1);
}
#[test]
fn write_read_cycle_multiple() {
let messages = [
Message {
sender_id: 42,
opcode: 0,
args: smallvec![
Argument::Int(42),
Argument::Str(Box::new(CString::new(&b"I like trains"[..]).unwrap())),
],
},
Message {
sender_id: 42,
opcode: 1,
args: smallvec![
Argument::Fd(1), // stdin
Argument::Fd(0), // stdout
],
},
Message {
sender_id: 42,
opcode: 2,
args: smallvec![
Argument::Uint(3),
Argument::Fd(2), // stderr
],
},
];
static SIGNATURES: &'static [&'static [ArgumentType]] = &[
&[ArgumentType::Int, ArgumentType::Str],
&[ArgumentType::Fd, ArgumentType::Fd],
&[ArgumentType::Uint, ArgumentType::Fd],
];
let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap();
let mut client = BufferedSocket::new(unsafe { Socket::from_raw_fd(client.into_raw_fd()) });
let mut server = BufferedSocket::new(unsafe { Socket::from_raw_fd(server.into_raw_fd()) });
for msg in &messages {
client.write_message(msg).unwrap();
}
client.flush().unwrap();
let mut recv_msgs = Vec::new();
let ret = server
.read_messages(
|sender_id, opcode| {
if sender_id == 42 {
Some(SIGNATURES[opcode as usize])
} else {
None
}
},
|message| {
recv_msgs.push(message);
true
},
)
.unwrap()
.unwrap();
assert_eq!(ret, 3);
assert_eq!(recv_msgs.len(), 3);
for (msg1, msg2) in messages.iter().zip(recv_msgs.iter()) {
assert_eq_msgs(msg1, msg2);
}
}
#[test]
fn parse_with_string_len_multiple_of_4() {
let msg = Message {
sender_id: 2,
opcode: 0,
args: smallvec![
Argument::Uint(18),
Argument::Str(Box::new(CString::new(&b"wl_shell"[..]).unwrap())),
Argument::Uint(1),
],
};
let (client, server) = ::std::os::unix::net::UnixStream::pair().unwrap();
let mut client = BufferedSocket::new(unsafe { Socket::from_raw_fd(client.into_raw_fd()) });
let mut server = BufferedSocket::new(unsafe { Socket::from_raw_fd(server.into_raw_fd()) });
client.write_message(&msg).unwrap();
client.flush().unwrap();
static SIGNATURE: &'static [ArgumentType] =
&[ArgumentType::Uint, ArgumentType::Str, ArgumentType::Uint];
let ret = server
.read_messages(
|sender_id, opcode| {
if sender_id == 2 && opcode == 0 {
Some(SIGNATURE)
} else {
None
}
},
|message| {
assert_eq_msgs(&message, &msg);
true
},
)
.unwrap()
.unwrap();
assert_eq!(ret, 1);
}
}

View file

@ -0,0 +1,325 @@
//! Various utilities used for other implementations
use once_cell::sync::OnceCell;
use std::any::Any;
use std::mem::ManuallyDrop;
use std::thread::{self, ThreadId};
use self::list::AppendList;
/// A wrapper for user data, able to store any type, and correctly
/// handling access from a wrong thread
#[derive(Debug)]
pub struct UserData {
inner: OnceCell<UserDataInner>,
}
#[derive(Debug)]
enum UserDataInner {
ThreadSafe(Box<dyn Any + Send + Sync + 'static>),
NonThreadSafe(Box<ManuallyDrop<dyn Any + 'static>>, ThreadId),
}
// UserData itself is always threadsafe, as it only gives access to its
// content if it is send+sync or we are on the right thread
unsafe impl Send for UserData {}
unsafe impl Sync for UserData {}
impl UserData {
/// Create a new UserData instance
pub const fn new() -> UserData {
UserData { inner: OnceCell::new() }
}
/// Sets the UserData to a given value
///
/// The provided closure is called to init the UserData,
/// does nothing is the UserData had already been set.
pub fn set<T: Any + 'static, F: FnOnce() -> T>(&self, f: F) {
self.inner.get_or_init(|| {
UserDataInner::NonThreadSafe(Box::new(ManuallyDrop::new(f())), thread::current().id())
});
}
/// Sets the UserData to a given threadsafe value
///
/// The provided closure is called to init the UserData,
/// does nothing is the UserData had already been set.
pub fn set_threadsafe<T: Any + Send + Sync + 'static, F: FnOnce() -> T>(&self, f: F) {
self.inner.get_or_init(|| UserDataInner::ThreadSafe(Box::new(f())));
}
/// Attempt to access the wrapped user data
///
/// Will return `None` if either:
///
/// - The requested type `T` does not match the type used for construction
/// - This `UserData` has been created using the non-threadsafe variant and access
/// is attempted from an other thread than the one it was created on
pub fn get<T: 'static>(&self) -> Option<&T> {
match self.inner.get() {
Some(&UserDataInner::ThreadSafe(ref val)) => <dyn Any>::downcast_ref::<T>(&**val),
Some(&UserDataInner::NonThreadSafe(ref val, threadid)) => {
// only give access if we are on the right thread
if threadid == thread::current().id() {
<dyn Any>::downcast_ref::<T>(&***val)
} else {
None
}
}
None => None,
}
}
}
impl Drop for UserData {
fn drop(&mut self) {
// only drop non-Send user data if we are on the right thread, leak it otherwise
if let Some(&mut UserDataInner::NonThreadSafe(ref mut val, threadid)) = self.inner.get_mut()
{
if threadid == thread::current().id() {
unsafe {
ManuallyDrop::drop(&mut **val);
}
}
}
}
}
/// A storage able to store several values of `UserData`
/// of different types. It behaves similarly to a `TypeMap`.
#[derive(Debug)]
pub struct UserDataMap {
list: AppendList<UserData>,
}
impl UserDataMap {
/// Create a new map
pub fn new() -> UserDataMap {
UserDataMap { list: AppendList::new() }
}
/// Attempt to access the wrapped user data of a given type
///
/// Will return `None` if no value of type `T` is stored in this `UserDataMap`
/// and accessible from this thread
pub fn get<T: 'static>(&self) -> Option<&T> {
for user_data in &self.list {
if let Some(val) = user_data.get::<T>() {
return Some(val);
}
}
None
}
/// Insert a value in the map if it is not already there
///
/// This is the non-threadsafe variant, the type you insert don't have to be
/// threadsafe, but they will not be visible from other threads (even if they are
/// actually threadsafe).
///
/// If the value does not already exists, the closure is called to create it and
/// this function returns `true`. If the value already exists, the closure is not
/// called, and this function returns `false`.
pub fn insert_if_missing<T: 'static, F: FnOnce() -> T>(&self, init: F) -> bool {
if self.get::<T>().is_some() {
return false;
}
let data = UserData::new();
data.set(init);
self.list.append(data);
true
}
/// Insert a value in the map if it is not already there
///
/// This is the threadsafe variant, the type you insert must be threadsafe and will
/// be visible from all threads.
///
/// If the value does not already exists, the closure is called to create it and
/// this function returns `true`. If the value already exists, the closure is not
/// called, and this function returns `false`.
pub fn insert_if_missing_threadsafe<T: Send + Sync + 'static, F: FnOnce() -> T>(
&self,
init: F,
) -> bool {
if self.get::<T>().is_some() {
return false;
}
let data = UserData::new();
data.set_threadsafe(init);
self.list.append(data);
true
}
}
impl Default for UserDataMap {
fn default() -> UserDataMap {
UserDataMap::new()
}
}
mod list {
/*
* This is a lock-free append-only list, it is used as an implementation
* detail of the UserDataMap.
*
* It was extracted from https://github.com/Diggsey/lockless under MIT license
* Copyright © Diggory Blake <diggsey@googlemail.com>
*/
use std::sync::atomic::{AtomicPtr, Ordering};
use std::{mem, ptr};
type NodePtr<T> = Option<Box<Node<T>>>;
#[derive(Debug)]
struct Node<T> {
value: T,
next: AppendList<T>,
}
#[derive(Debug)]
pub struct AppendList<T>(AtomicPtr<Node<T>>);
impl<T> AppendList<T> {
fn node_into_raw(ptr: NodePtr<T>) -> *mut Node<T> {
match ptr {
Some(b) => Box::into_raw(b),
None => ptr::null_mut(),
}
}
unsafe fn node_from_raw(ptr: *mut Node<T>) -> NodePtr<T> {
if ptr.is_null() {
None
} else {
Some(Box::from_raw(ptr))
}
}
fn new_internal(ptr: NodePtr<T>) -> Self {
AppendList(AtomicPtr::new(Self::node_into_raw(ptr)))
}
pub fn new() -> Self {
Self::new_internal(None)
}
pub fn append(&self, value: T) {
self.append_list(AppendList::new_internal(Some(Box::new(Node {
value,
next: AppendList::new(),
}))));
}
unsafe fn append_ptr(&self, p: *mut Node<T>) {
loop {
match self.0.compare_exchange_weak(
ptr::null_mut(),
p,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => return,
Err(head) => {
if !head.is_null() {
return (*head).next.append_ptr(p);
}
}
}
}
}
pub fn append_list(&self, other: AppendList<T>) {
let p = other.0.load(Ordering::Acquire);
mem::forget(other);
unsafe { self.append_ptr(p) };
}
pub fn iter(&self) -> AppendListIterator<T> {
AppendListIterator(&self.0)
}
pub fn iter_mut(&mut self) -> AppendListMutIterator<T> {
AppendListMutIterator(&mut self.0)
}
}
impl<'a, T> IntoIterator for &'a AppendList<T> {
type Item = &'a T;
type IntoIter = AppendListIterator<'a, T>;
fn into_iter(self) -> AppendListIterator<'a, T> {
self.iter()
}
}
impl<'a, T> IntoIterator for &'a mut AppendList<T> {
type Item = &'a mut T;
type IntoIter = AppendListMutIterator<'a, T>;
fn into_iter(self) -> AppendListMutIterator<'a, T> {
self.iter_mut()
}
}
impl<T> Drop for AppendList<T> {
fn drop(&mut self) {
unsafe { Self::node_from_raw(mem::replace(self.0.get_mut(), ptr::null_mut())) };
}
}
#[derive(Debug)]
pub struct AppendListIterator<'a, T: 'a>(&'a AtomicPtr<Node<T>>);
impl<'a, T: 'a> Iterator for AppendListIterator<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
let p = self.0.load(Ordering::Acquire);
if p.is_null() {
None
} else {
unsafe {
self.0 = &(*p).next.0;
Some(&(*p).value)
}
}
}
}
#[derive(Debug)]
pub struct AppendListMutIterator<'a, T: 'a>(&'a mut AtomicPtr<Node<T>>);
impl<'a, T: 'a> Iterator for AppendListMutIterator<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<&'a mut T> {
let p = self.0.load(Ordering::Acquire);
if p.is_null() {
None
} else {
unsafe {
self.0 = &mut (*p).next.0;
Some(&mut (*p).value)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::UserDataMap;
#[test]
fn insert_twice() {
let map = UserDataMap::new();
assert_eq!(map.get::<usize>(), None);
assert!(map.insert_if_missing(|| 42usize));
assert!(!map.insert_if_missing(|| 43usize));
assert_eq!(map.get::<usize>(), Some(&42));
}
}

View file

@ -0,0 +1,460 @@
//! Types and routines used to manipulate arguments from the wire format
use std::ffi::{CStr, CString};
use std::os::unix::io::RawFd;
use std::ptr;
use nix::{Error as NixError, Result as NixResult};
use smallvec::SmallVec;
// The value of 4 is chosen for the following reasons:
// - almost all messages have 4 arguments or less
// - there are some potentially spammy events that have 3/4 arguments (wl_touch.move has 4 for example)
//
// This brings the size of Message to 11*usize (instead of 4*usize with a regular vec), but eliminates
// almost all allocations that may occur during the processing of messages, both client-side and server-side.
const INLINE_ARGS: usize = 4;
/// Wire metadata of a given message
#[derive(Copy, Clone, Debug)]
pub struct MessageDesc {
/// Name of this message
pub name: &'static str,
/// Signature of the message
pub signature: &'static [ArgumentType],
/// Minimum required version of the interface
pub since: u32,
/// Whether this message is a destructor
pub destructor: bool,
}
/// Enum of possible argument types as recognized by the wire
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum ArgumentType {
/// i32
Int,
/// u32
Uint,
/// fixed point, 1/256 precision
Fixed,
/// CString
Str,
/// id of a wayland object
Object,
/// id of a newly created wayland object
NewId,
/// Vec<u8>
Array,
/// RawFd
Fd,
}
/// Enum of possible argument as recognized by the wire, including values
#[derive(Clone, PartialEq, Debug)]
#[allow(clippy::box_collection)]
pub enum Argument {
/// i32
Int(i32),
/// u32
Uint(u32),
/// fixed point, 1/256 precision
Fixed(i32),
/// CString
///
/// The value is boxed to reduce the stack size of Argument. The performance
/// impact is negligible as `string` arguments are pretty rare in the protocol.
Str(Box<CString>),
/// id of a wayland object
Object(u32),
/// id of a newly created wayland object
NewId(u32),
/// Vec<u8>
///
/// The value is boxed to reduce the stack size of Argument. The performance
/// impact is negligible as `array` arguments are pretty rare in the protocol.
Array(Box<Vec<u8>>),
/// RawFd
Fd(RawFd),
}
impl Argument {
/// Retrieve the type of a given argument instance
pub fn get_type(&self) -> ArgumentType {
match *self {
Argument::Int(_) => ArgumentType::Int,
Argument::Uint(_) => ArgumentType::Uint,
Argument::Fixed(_) => ArgumentType::Fixed,
Argument::Str(_) => ArgumentType::Str,
Argument::Object(_) => ArgumentType::Object,
Argument::NewId(_) => ArgumentType::NewId,
Argument::Array(_) => ArgumentType::Array,
Argument::Fd(_) => ArgumentType::Fd,
}
}
}
impl std::fmt::Display for Argument {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Argument::Int(value) => write!(f, "{}", value),
Argument::Uint(value) => write!(f, "{}", value),
Argument::Fixed(value) => write!(f, "{}", value),
Argument::Str(value) => write!(f, "{:?}", value),
Argument::Object(value) => write!(f, "{}", value),
Argument::NewId(value) => write!(f, "{}", value),
Argument::Array(value) => write!(f, "{:?}", value),
Argument::Fd(value) => write!(f, "{}", value),
}
}
}
/// A wire message
#[derive(Debug, Clone, PartialEq)]
pub struct Message {
/// ID of the object sending this message
pub sender_id: u32,
/// Opcode of the message
pub opcode: u16,
/// Arguments of the message
pub args: SmallVec<[Argument; INLINE_ARGS]>,
}
/// Error generated when trying to serialize a message into buffers
#[derive(Debug, Clone)]
pub enum MessageWriteError {
/// The buffer is too small to hold the message contents
BufferTooSmall,
/// The message contains a FD that could not be dup-ed
DupFdFailed(::nix::Error),
}
impl std::error::Error for MessageWriteError {}
impl std::fmt::Display for MessageWriteError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
MessageWriteError::BufferTooSmall => {
f.write_str("The provided buffer is too small to hold message content.")
}
MessageWriteError::DupFdFailed(_) => {
f.write_str("The message contains a file descriptor that could not be dup()-ed.")
}
}
}
}
/// Error generated when trying to deserialize a message from buffers
#[derive(Debug, Clone)]
pub enum MessageParseError {
/// The message references a FD but the buffer FD is empty
MissingFD,
/// More data is needed to deserialize the message
MissingData,
/// The message is malformed and cannot be parsed
Malformed,
}
impl std::error::Error for MessageParseError {}
impl std::fmt::Display for MessageParseError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
MessageParseError::MissingFD => {
f.write_str("The message references a FD but the buffer FD is empty.")
}
MessageParseError::MissingData => {
f.write_str("More data is needed to deserialize the message")
}
MessageParseError::Malformed => {
f.write_str("The message is malformed and cannot be parsed")
}
}
}
}
impl Message {
/// Serialize the contents of this message into provided buffers
///
/// Returns the number of elements written in each buffer
///
/// Any serialized Fd will be `dup()`-ed in the process
pub fn write_to_buffers(
&self,
payload: &mut [u32],
mut fds: &mut [RawFd],
) -> Result<(usize, usize), MessageWriteError> {
let orig_payload_len = payload.len();
let orig_fds_len = fds.len();
// Helper function to write a u32 or a RawFd to its buffer
fn write_buf<T>(u: T, payload: &mut [T]) -> Result<&mut [T], MessageWriteError> {
if let Some((head, tail)) = payload.split_first_mut() {
*head = u;
Ok(tail)
} else {
Err(MessageWriteError::BufferTooSmall)
}
}
// Helper function to write byte arrays in payload
fn write_array_to_payload<'a>(
array: &[u8],
payload: &'a mut [u32],
) -> Result<&'a mut [u32], MessageWriteError> {
let array_len = array.len();
let word_len = array_len / 4 + if array_len % 4 != 0 { 1 } else { 0 };
// need enough space to store the whole array with padding and a size header
if payload.len() < 1 + word_len {
return Err(MessageWriteError::BufferTooSmall);
}
// size header
payload[0] = array_len as u32;
let (buffer_slice, rest) = payload[1..].split_at_mut(word_len);
unsafe {
ptr::copy(array.as_ptr(), buffer_slice.as_mut_ptr() as *mut u8, array_len);
}
Ok(rest)
}
let free_size = payload.len();
if free_size < 2 {
return Err(MessageWriteError::BufferTooSmall);
}
let (header, mut payload) = payload.split_at_mut(2);
// we store all fds we dup-ed in this, which will auto-close
// them on drop, if any of the `?` early-returns
let mut pending_fds = FdStore::new();
// write the contents in the buffer
for arg in &self.args {
// Just to make the borrow checker happy
let old_payload = payload;
match *arg {
Argument::Int(i) => payload = write_buf(i as u32, old_payload)?,
Argument::Uint(u) => payload = write_buf(u, old_payload)?,
Argument::Fixed(f) => payload = write_buf(f as u32, old_payload)?,
Argument::Str(ref s) => {
payload = write_array_to_payload(s.as_bytes_with_nul(), old_payload)?;
}
Argument::Object(o) => payload = write_buf(o, old_payload)?,
Argument::NewId(n) => payload = write_buf(n, old_payload)?,
Argument::Array(ref a) => {
payload = write_array_to_payload(a, old_payload)?;
}
Argument::Fd(fd) => {
let old_fds = fds;
let dup_fd = dup_fd_cloexec(fd).map_err(MessageWriteError::DupFdFailed)?;
pending_fds.push(dup_fd);
fds = write_buf(dup_fd, old_fds)?;
payload = old_payload;
}
}
}
// we reached here, all writing was successful
// no FD needs to be closed
pending_fds.clear();
let wrote_size = (free_size - payload.len()) * 4;
header[0] = self.sender_id;
header[1] = ((wrote_size as u32) << 16) | u32::from(self.opcode);
Ok((orig_payload_len - payload.len(), orig_fds_len - fds.len()))
}
/// Attempts to parse a single wayland message with the given signature.
///
/// If the buffers contains several messages, only the first one will be parsed,
/// and the unused tail of the buffers is returned. If a single message was present,
/// the returned slices should thus be empty.
///
/// Errors if the message is malformed.
pub fn from_raw<'a, 'b>(
raw: &'a [u32],
signature: &[ArgumentType],
fds: &'b [RawFd],
) -> Result<(Message, &'a [u32], &'b [RawFd]), MessageParseError> {
// helper function to read arrays
fn read_array_from_payload(
array_len: usize,
payload: &[u32],
) -> Result<(&[u8], &[u32]), MessageParseError> {
let word_len = array_len / 4 + if array_len % 4 != 0 { 1 } else { 0 };
if word_len > payload.len() {
return Err(MessageParseError::MissingData);
}
let (array_contents, rest) = payload.split_at(word_len);
let array = unsafe {
::std::slice::from_raw_parts(array_contents.as_ptr() as *const u8, array_len)
};
Ok((array, rest))
}
if raw.len() < 2 {
return Err(MessageParseError::MissingData);
}
let sender_id = raw[0];
let word_2 = raw[1];
let opcode = (word_2 & 0x0000_FFFF) as u16;
let len = (word_2 >> 16) as usize / 4;
if len < 2 || len > raw.len() {
return Err(MessageParseError::Malformed);
}
let (mut payload, rest) = raw.split_at(len);
payload = &payload[2..];
let mut fds = fds;
let arguments = signature
.iter()
.map(|argtype| {
if let ArgumentType::Fd = *argtype {
// don't consume input but fd
if let Some((&front, tail)) = fds.split_first() {
fds = tail;
Ok(Argument::Fd(front))
} else {
Err(MessageParseError::MissingFD)
}
} else if let Some((&front, mut tail)) = payload.split_first() {
let arg = match *argtype {
ArgumentType::Int => Ok(Argument::Int(front as i32)),
ArgumentType::Uint => Ok(Argument::Uint(front)),
ArgumentType::Fixed => Ok(Argument::Fixed(front as i32)),
ArgumentType::Str => read_array_from_payload(front as usize, tail)
.and_then(|(v, rest)| {
tail = rest;
match CStr::from_bytes_with_nul(v) {
Ok(s) => Ok(Argument::Str(Box::new(s.into()))),
Err(_) => Err(MessageParseError::Malformed),
}
}),
ArgumentType::Object => Ok(Argument::Object(front)),
ArgumentType::NewId => Ok(Argument::NewId(front)),
ArgumentType::Array => {
read_array_from_payload(front as usize, tail).map(|(v, rest)| {
tail = rest;
Argument::Array(Box::new(v.into()))
})
}
ArgumentType::Fd => unreachable!(),
};
payload = tail;
arg
} else {
Err(MessageParseError::MissingData)
}
})
.collect::<Result<SmallVec<_>, MessageParseError>>()?;
let msg = Message { sender_id, opcode, args: arguments };
Ok((msg, rest, fds))
}
}
/// Duplicate a `RawFd` and set the CLOEXEC flag on the copy
pub fn dup_fd_cloexec(fd: RawFd) -> NixResult<RawFd> {
use nix::fcntl;
match fcntl::fcntl(fd, fcntl::FcntlArg::F_DUPFD_CLOEXEC(0)) {
Ok(newfd) => Ok(newfd),
Err(NixError::EINVAL) => {
// F_DUPFD_CLOEXEC is not recognized, kernel too old, fallback
// to setting CLOEXEC manually
let newfd = fcntl::fcntl(fd, fcntl::FcntlArg::F_DUPFD(0))?;
let flags = fcntl::fcntl(newfd, fcntl::FcntlArg::F_GETFD);
let result = flags
.map(|f| fcntl::FdFlag::from_bits(f).unwrap() | fcntl::FdFlag::FD_CLOEXEC)
.and_then(|f| fcntl::fcntl(newfd, fcntl::FcntlArg::F_SETFD(f)));
match result {
Ok(_) => {
// setting the O_CLOEXEC worked
Ok(newfd)
}
Err(e) => {
// something went wrong in F_GETFD or F_SETFD
let _ = ::nix::unistd::close(newfd);
Err(e)
}
}
}
Err(e) => Err(e),
}
}
/*
* utility struct that closes every FD it contains on drop
*/
struct FdStore {
fds: Vec<RawFd>,
}
impl FdStore {
fn new() -> FdStore {
FdStore { fds: Vec::new() }
}
fn push(&mut self, fd: RawFd) {
self.fds.push(fd);
}
fn clear(&mut self) {
self.fds.clear();
}
}
impl Drop for FdStore {
fn drop(&mut self) {
use nix::unistd::close;
for fd in self.fds.drain(..) {
// not much can be done if we can't close that anyway...
let _ = close(fd);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use smallvec::smallvec;
#[test]
fn into_from_raw_cycle() {
let mut bytes_buffer = vec![0; 1024];
let mut fd_buffer = vec![0; 10];
let msg = Message {
sender_id: 42,
opcode: 7,
args: smallvec![
Argument::Uint(3),
Argument::Fixed(-89),
Argument::Str(Box::new(CString::new(&b"I like trains!"[..]).unwrap())),
Argument::Array(vec![1, 2, 3, 4, 5, 6, 7, 8, 9].into()),
Argument::Object(88),
Argument::NewId(56),
Argument::Int(-25),
],
};
// write the message to the buffers
msg.write_to_buffers(&mut bytes_buffer[..], &mut fd_buffer[..]).unwrap();
// read them back
let (rebuilt, _, _) = Message::from_raw(
&bytes_buffer[..],
&[
ArgumentType::Uint,
ArgumentType::Fixed,
ArgumentType::Str,
ArgumentType::Array,
ArgumentType::Object,
ArgumentType::NewId,
ArgumentType::Int,
],
&fd_buffer[..],
)
.unwrap();
assert_eq!(rebuilt, msg);
}
}