// Copyright 2017 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! Type-safe bindings for Zircon channel objects. use {AsHandleRef, HandleBased, Handle, HandleRef, Peered, Status, Time, usize_into_u32, size_to_u32_sat}; use {sys, ok}; use std::mem; /// An object representing a Zircon /// [channel](https://fuchsia.googlesource.com/zircon/+/master/docs/objects/channel.md). /// /// As essentially a subtype of `Handle`, it can be freely interconverted. #[derive(Debug, Eq, PartialEq, Hash)] pub struct Channel(Handle); impl_handle_based!(Channel); impl Peered for Channel {} impl Channel { /// Create a channel, resulting an a pair of `Channel` objects representing both /// sides of the channel. Messages written into one maybe read from the opposite. /// /// Wraps the /// [zx_channel_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/channel_create.md) /// syscall. pub fn create() -> Result<(Channel, Channel), Status> { unsafe { let mut handle0 = 0; let mut handle1 = 0; let opts = 0; ok(sys::zx_channel_create(opts, &mut handle0, &mut handle1))?; Ok(( Self::from(Handle::from_raw(handle0)), Self::from(Handle::from_raw(handle1)) )) } } /// Read a message from a channel. Wraps the /// [zx_channel_read](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/channel_read.md) /// syscall. /// /// If the `MessageBuf` lacks the capacity to hold the pending message, /// returns an `Err` with the number of bytes and number of handles needed. /// Otherwise returns an `Ok` with the result as usual. pub fn read_raw(&self, buf: &mut MessageBuf) -> Result, (usize, usize)> { let opts = 0; unsafe { buf.clear(); let raw_handle = self.raw_handle(); let mut num_bytes: u32 = size_to_u32_sat(buf.bytes.capacity()); let mut num_handles: u32 = size_to_u32_sat(buf.handles.capacity()); let status = ok(sys::zx_channel_read(raw_handle, opts, buf.bytes.as_mut_ptr(), buf.handles.as_mut_ptr() as *mut _, num_bytes, num_handles, &mut num_bytes, &mut num_handles)); if status == Err(Status::BUFFER_TOO_SMALL) { Err((num_bytes as usize, num_handles as usize)) } else { Ok(status.map(|()| { buf.bytes.set_len(num_bytes as usize); buf.handles.set_len(num_handles as usize); })) } } } /// Read a message from a channel. /// /// Note that this method can cause internal reallocations in the `MessageBuf` /// if it is lacks capacity to hold the full message. If such reallocations /// are not desirable, use `read_raw` instead. pub fn read(&self, buf: &mut MessageBuf) -> Result<(), Status> { loop { match self.read_raw(buf) { Ok(result) => return result, Err((num_bytes, num_handles)) => { buf.ensure_capacity_bytes(num_bytes); buf.ensure_capacity_handles(num_handles); } } } } /// Write a message to a channel. Wraps the /// [zx_channel_write](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/channel_write.md) /// syscall. pub fn write(&self, bytes: &[u8], handles: &mut Vec) -> Result<(), Status> { let opts = 0; let n_bytes = try!(usize_into_u32(bytes.len()).map_err(|_| Status::OUT_OF_RANGE)); let n_handles = try!(usize_into_u32(handles.len()).map_err(|_| Status::OUT_OF_RANGE)); unsafe { let status = sys::zx_channel_write(self.raw_handle(), opts, bytes.as_ptr(), n_bytes, handles.as_ptr() as *const sys::zx_handle_t, n_handles); ok(status)?; // Handles were successfully transferred, forget them on sender side handles.set_len(0); Ok(()) } } /// Send a message consisting of the given bytes and handles to a channel and await a reply. The /// bytes should start with a four byte 'txid' which is used to identify the matching reply. /// /// Wraps the /// [zx_channel_call](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/channel_call.md) /// syscall. /// /// Note that unlike [`read`][read], the caller must ensure that the MessageBuf has enough /// capacity for the bytes and handles which will be received, as replies which are too large /// are discarded. /// /// On failure returns the both the main and read status. /// /// [read]: struct.Channel.html#method.read pub fn call(&self, timeout: Time, bytes: &[u8], handles: &mut Vec, buf: &mut MessageBuf) -> Result<(), (Status, Status)> { let write_num_bytes = try!(usize_into_u32(bytes.len()).map_err( |_| (Status::OUT_OF_RANGE, Status::OK))); let write_num_handles = try!(usize_into_u32(handles.len()).map_err( |_| (Status::OUT_OF_RANGE, Status::OK))); buf.clear(); let read_num_bytes: u32 = size_to_u32_sat(buf.bytes.capacity()); let read_num_handles: u32 = size_to_u32_sat(buf.handles.capacity()); let args = sys::zx_channel_call_args_t { wr_bytes: bytes.as_ptr(), wr_handles: handles.as_ptr() as *const sys::zx_handle_t, rd_bytes: buf.bytes.as_mut_ptr(), rd_handles: buf.handles.as_mut_ptr() as *mut _, wr_num_bytes: write_num_bytes, wr_num_handles: write_num_handles, rd_num_bytes: read_num_bytes, rd_num_handles: read_num_handles, }; let mut actual_read_bytes: u32 = 0; let mut actual_read_handles: u32 = 0; let mut read_status = Status::OK.into_raw(); let options = 0; let status = unsafe { Status::from_raw( sys::zx_channel_call( self.raw_handle(), options, timeout.nanos(), &args, &mut actual_read_bytes, &mut actual_read_handles, &mut read_status)) }; match status { Status::OK | Status::TIMED_OUT | Status::CALL_FAILED => { // Handles were successfully transferred, // even if we didn't get a response, so forget // them on the sender side. unsafe { handles.set_len(0); } } _ => {} } unsafe { buf.bytes.set_len(actual_read_bytes as usize); buf.handles.set_len(actual_read_handles as usize); } if Status::OK == status { Ok(()) } else { Err((status, Status::from_raw(read_status))) } } } #[test] pub fn test_handle_repr() { assert_eq!(::std::mem::size_of::(), 4); assert_eq!(::std::mem::size_of::(), 4); assert_eq!(::std::mem::align_of::(), ::std::mem::align_of::()); // This test asserts that repr(transparent) still works for Handle -> zx_handle_t let n: Vec = vec![0, 100, 2<<32-1]; let v: Vec = n.iter().map(|h| unsafe { Handle::from_raw(*h) } ).collect(); for (handle, raw) in v.iter().zip(n.iter()) { unsafe { assert_eq!(*(handle as *const _ as *const [u8; 4]), *(raw as *const _ as *const [u8; 4])); } } for h in v.into_iter() { ::std::mem::forget(h); } } impl AsRef for Channel { fn as_ref(&self) -> &Self { &self } } /// A buffer for _receiving_ messages from a channel. /// /// A `MessageBuf` is essentially a byte buffer and a vector of /// handles, but move semantics for "taking" handles requires special handling. /// /// Note that for sending messages to a channel, the caller manages the buffers, /// using a plain byte slice and `Vec`. #[derive(Default)] #[derive(Debug)] pub struct MessageBuf { bytes: Vec, handles: Vec, } impl MessageBuf { /// Create a new, empty, message buffer. pub fn new() -> Self { Default::default() } /// Create a new non-empty message buffer. pub fn new_with(v: Vec, h: Vec) -> Self { Self{ bytes: v, handles: h, } } /// Ensure that the buffer has the capacity to hold at least `n_bytes` bytes. pub fn ensure_capacity_bytes(&mut self, n_bytes: usize) { ensure_capacity(&mut self.bytes, n_bytes); } /// Ensure that the buffer has the capacity to hold at least `n_handles` handles. pub fn ensure_capacity_handles(&mut self, n_handles: usize) { ensure_capacity(&mut self.handles, n_handles); } /// Ensure that at least n_bytes bytes are initialized (0 fill). pub fn ensure_initialized_bytes(&mut self, n_bytes: usize) { if n_bytes <= self.bytes.len() { return; } self.bytes.resize(n_bytes, 0); } /// Get a reference to the bytes of the message buffer, as a `&[u8]` slice. pub fn bytes(&self) -> &[u8] { self.bytes.as_slice() } /// The number of handles in the message buffer. Note this counts the number /// available when the message was received; `take_handle` does not affect /// the count. pub fn n_handles(&self) -> usize { self.handles.len() } /// Take the handle at the specified index from the message buffer. If the /// method is called again with the same index, it will return `None`, as /// will happen if the index exceeds the number of handles available. pub fn take_handle(&mut self, index: usize) -> Option { self.handles.get_mut(index).and_then(|handle| if handle.is_invalid() { None } else { Some(mem::replace(handle, Handle::invalid())) } ) } /// Clear the bytes and handles contained in the buf. This will drop any /// contained handles, resulting in their resources being freed. pub fn clear(&mut self) { self.bytes.clear(); self.handles.clear(); } } fn ensure_capacity(vec: &mut Vec, size: usize) { let len = vec.len(); if size > len { vec.reserve(size - len); } } #[cfg(test)] mod tests { use super::*; use {DurationNum, Rights, Signals, Vmo}; use std::thread; #[test] fn channel_basic() { let (p1, p2) = Channel::create().unwrap(); let mut empty = vec![]; assert!(p1.write(b"hello", &mut empty).is_ok()); let mut buf = MessageBuf::new(); assert!(p2.read(&mut buf).is_ok()); assert_eq!(buf.bytes(), b"hello"); } #[test] fn channel_read_raw_too_small() { let (p1, p2) = Channel::create().unwrap(); let mut empty = vec![]; assert!(p1.write(b"hello", &mut empty).is_ok()); let mut buf = MessageBuf::new(); let result = p2.read_raw(&mut buf); assert_eq!(result, Err((5, 0))); assert_eq!(buf.bytes(), b""); } #[test] fn channel_send_handle() { let hello_length: usize = 5; // Create a pair of channels and a virtual memory object. let (p1, p2) = Channel::create().unwrap(); let vmo = Vmo::create(hello_length as u64).unwrap(); // Duplicate VMO handle and send it down the channel. let duplicate_vmo_handle = vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap().into(); let mut handles_to_send: Vec = vec![duplicate_vmo_handle]; assert!(p1.write(b"", &mut handles_to_send).is_ok()); // Handle should be removed from vector. assert!(handles_to_send.is_empty()); // Read the handle from the receiving channel. let mut buf = MessageBuf::new(); assert!(p2.read(&mut buf).is_ok()); assert_eq!(buf.n_handles(), 1); // Take the handle from the buffer. let received_handle = buf.take_handle(0).unwrap(); // Should not affect number of handles. assert_eq!(buf.n_handles(), 1); // Trying to take it again should fail. assert!(buf.take_handle(0).is_none()); // Now to test that we got the right handle, try writing something to it... let received_vmo = Vmo::from(received_handle); assert_eq!(received_vmo.write(b"hello", 0).unwrap(), hello_length); // ... and reading it back from the original VMO. let mut read_vec = vec![0; hello_length]; assert_eq!(vmo.read(&mut read_vec, 0).unwrap(), hello_length); assert_eq!(read_vec, b"hello"); } #[test] fn channel_call_timeout() { let ten_ms = 10.millis(); // Create a pair of channels and a virtual memory object. let (p1, p2) = Channel::create().unwrap(); let vmo = Vmo::create(0 as u64).unwrap(); // Duplicate VMO handle and send it along with the call. let duplicate_vmo_handle = vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap().into(); let mut handles_to_send: Vec = vec![duplicate_vmo_handle]; let mut buf = MessageBuf::new(); assert_eq!(p1.call(ten_ms.after_now(), b"call", &mut handles_to_send, &mut buf), Err((Status::TIMED_OUT, Status::OK))); // Handle should be removed from vector even though we didn't get a response, as it was // still sent over the channel. assert!(handles_to_send.is_empty()); // Should be able to read call even though it timed out waiting for a response. let mut buf = MessageBuf::new(); assert!(p2.read(&mut buf).is_ok()); assert_eq!(buf.bytes(), b"call"); assert_eq!(buf.n_handles(), 1); } #[test] fn channel_call() { // Create a pair of channels let (p1, p2) = Channel::create().unwrap(); // create an mpsc channel for communicating the call data for later assertion let (tx, rx) = ::std::sync::mpsc::channel(); // Start a new thread to respond to the call. thread::spawn(move || { let mut buf = MessageBuf::new(); // if either the read or the write fail, this thread will panic, // resulting in tx being dropped, which will be noticed by the rx. p2.wait_handle(Signals::CHANNEL_READABLE, 1.seconds().after_now()).expect("callee wait error"); p2.read(&mut buf).expect("callee read error"); p2.write(b"txidresponse", &mut vec![]).expect("callee write error"); tx.send(buf).expect("callee mpsc send error"); }); // Make the call. let mut buf = MessageBuf::new(); buf.ensure_capacity_bytes(12); // NOTE(raggi): CQ has been seeing some long stalls from channel call, // and it's as yet unclear why. The timeout here has been made much // larger in order to avoid that, as the issues are not issues with this // crate's concerns. The timeout is here just to prevent the tests from // stalling forever if a developer makes a mistake locally in this // crate. Tests of Zircon behavior or virtualization behavior should be // covered elsewhere. See ZX-1324. p1.call(30.seconds().after_now(), b"txidcall", &mut vec![], &mut buf).expect("channel call error"); assert_eq!(buf.bytes(), b"txidresponse"); assert_eq!(buf.n_handles(), 0); let sbuf = rx.recv().expect("mpsc channel recv error"); assert_eq!(sbuf.bytes(), b"txidcall"); assert_eq!(sbuf.n_handles(), 0); } }