use std::mem;
use std::string::ToString;
use base::AsRawDescriptor;
use base::RawDescriptor;
use zerocopy::AsBytes;
use crate::message::*;
use crate::BackendReq;
use crate::Connection;
use crate::Error;
use crate::Frontend;
use crate::HandlerResult;
use crate::Result;
pub struct FrontendClient {
sock: Connection<BackendReq>,
reply_ack_negotiated: bool,
error: Option<i32>,
}
impl FrontendClient {
pub fn new(ep: Connection<BackendReq>) -> Self {
FrontendClient {
sock: ep,
reply_ack_negotiated: false,
error: None,
}
}
fn send_message<T>(
&mut self,
request: BackendReq,
msg: &T,
fds: Option<&[RawDescriptor]>,
) -> HandlerResult<u64>
where
T: AsBytes,
{
let len = mem::size_of::<T>();
let mut hdr = VhostUserMsgHeader::new(request, 0, len as u32);
if self.reply_ack_negotiated {
hdr.set_need_reply(true);
}
self.sock
.send_message(&hdr, msg, fds)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
self.wait_for_reply(&hdr)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))
}
fn wait_for_reply(&mut self, hdr: &VhostUserMsgHeader<BackendReq>) -> Result<u64> {
let code = hdr.get_code().map_err(|_| Error::InvalidMessage)?;
if code != BackendReq::SHMEM_MAP
&& code != BackendReq::SHMEM_UNMAP
&& code != BackendReq::GPU_MAP
&& code != BackendReq::EXTERNAL_MAP
&& !self.reply_ack_negotiated
{
return Ok(0);
}
let (reply, body, rfds) = self.sock.recv_message::<VhostUserU64>()?;
if !reply.is_reply_for(hdr) || !rfds.is_empty() || !body.is_valid() {
return Err(Error::InvalidMessage);
}
if body.value != 0 {
return Err(Error::FrontendInternalError);
}
Ok(body.value)
}
pub fn set_reply_ack_flag(&mut self, enable: bool) {
self.reply_ack_negotiated = enable;
}
pub fn set_failed(&mut self, error: i32) {
self.error = Some(error);
}
}
impl Frontend for FrontendClient {
fn shmem_map(
&mut self,
req: &VhostUserShmemMapMsg,
fd: &dyn AsRawDescriptor,
) -> HandlerResult<u64> {
self.send_message(BackendReq::SHMEM_MAP, req, Some(&[fd.as_raw_descriptor()]))
}
fn shmem_unmap(&mut self, req: &VhostUserShmemUnmapMsg) -> HandlerResult<u64> {
self.send_message(BackendReq::SHMEM_UNMAP, req, None)
}
fn handle_config_change(&mut self) -> HandlerResult<u64> {
self.send_message(BackendReq::CONFIG_CHANGE_MSG, &VhostUserEmptyMessage, None)
}
fn gpu_map(
&mut self,
req: &VhostUserGpuMapMsg,
descriptor: &dyn AsRawDescriptor,
) -> HandlerResult<u64> {
self.send_message(
BackendReq::GPU_MAP,
req,
Some(&[descriptor.as_raw_descriptor()]),
)
}
fn external_map(&mut self, req: &VhostUserExternalMapMsg) -> HandlerResult<u64> {
self.send_message(BackendReq::EXTERNAL_MAP, req, None)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_backend_req_set_failed() {
let (p1, _p2) = Connection::pair().unwrap();
let mut frontend_client = FrontendClient::new(p1);
assert!(frontend_client.error.is_none());
frontend_client.set_failed(libc::EAGAIN);
assert_eq!(frontend_client.error, Some(libc::EAGAIN));
}
}