xref: /aosp_15_r20/external/crosvm/devices/src/virtio/vhost/user/device/connection/sys/linux/listener.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2024 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::pin::Pin;
6 
7 use anyhow::Context;
8 use base::AsRawDescriptor;
9 use base::RawDescriptor;
10 use cros_async::AsyncWrapper;
11 use cros_async::Executor;
12 use futures::Future;
13 use futures::FutureExt;
14 use vmm_vhost::connection::Listener;
15 use vmm_vhost::unix::SocketListener;
16 use vmm_vhost::BackendServer;
17 
18 use crate::virtio::vhost::user::device::connection::VhostUserConnectionTrait;
19 use crate::virtio::vhost::user::device::handler::sys::linux::run_handler;
20 
21 /// On Unix we can listen to a socket.
22 pub struct VhostUserListener(SocketListener);
23 
24 impl VhostUserListener {
25     /// Create a vhost-user listener from a UNIX domain socket path.
new(path: &str) -> anyhow::Result<Self>26     pub fn new(path: &str) -> anyhow::Result<Self> {
27         let listener = SocketListener::new(path, true)?;
28 
29         Ok(VhostUserListener(listener))
30     }
31 }
32 
33 impl AsRawDescriptor for VhostUserListener {
as_raw_descriptor(&self) -> RawDescriptor34     fn as_raw_descriptor(&self) -> RawDescriptor {
35         self.0.as_raw_descriptor()
36     }
37 }
38 
39 /// Attaches to an already bound socket via `listener` and handles incoming messages from the
40 /// VMM, which are dispatched to the device backend via the `VhostUserDevice` trait methods.
run_with_handler( mut listener: SocketListener, handler: Box<dyn vmm_vhost::Backend>, ex: &Executor, ) -> anyhow::Result<()>41 async fn run_with_handler(
42     mut listener: SocketListener,
43     handler: Box<dyn vmm_vhost::Backend>,
44     ex: &Executor,
45 ) -> anyhow::Result<()> {
46     listener.set_nonblocking(true)?;
47 
48     loop {
49         // If the listener is not ready on the first call to `accept` and returns `None`, we
50         // temporarily convert it into an async I/O source and yield until it signals there is
51         // input data awaiting, before trying again.
52         match listener
53             .accept()
54             .context("failed to accept an incoming connection")?
55         {
56             Some(connection) => {
57                 let req_handler = BackendServer::new(connection, handler);
58                 return run_handler(req_handler, ex).await;
59             }
60             None => {
61                 // Nobody is on the other end yet, wait until we get a connection.
62                 let async_waiter = ex
63                     .async_from(AsyncWrapper::new(listener))
64                     .context("failed to create async waiter")?;
65                 async_waiter.wait_readable().await?;
66 
67                 // Retrieve the listener back so we can use it again.
68                 listener = async_waiter.into_source().into_inner();
69             }
70         }
71     }
72 }
73 
74 impl VhostUserConnectionTrait for VhostUserListener {
run_req_handler<'e>( self, handler: Box<dyn vmm_vhost::Backend>, ex: &'e Executor, ) -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + 'e>>75     fn run_req_handler<'e>(
76         self,
77         handler: Box<dyn vmm_vhost::Backend>,
78         ex: &'e Executor,
79     ) -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + 'e>> {
80         async { run_with_handler(self.0, handler, ex).await }.boxed_local()
81     }
82 
take_parent_process_resources(&mut self) -> Option<Box<dyn std::any::Any>>83     fn take_parent_process_resources(&mut self) -> Option<Box<dyn std::any::Any>> {
84         self.0.take_resources_for_parent()
85     }
86 }
87