1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use anyhow::Context;
use anyhow::Result;
use base::info;
use base::AsRawDescriptor;
use base::SafeDescriptor;
use cros_async::AsyncWrapper;
use cros_async::Executor;
use vmm_vhost::BackendServer;
use vmm_vhost::Error as VhostError;

/// Performs the run loop for an already-constructor request handler.
pub async fn run_handler<S>(mut backend_server: BackendServer<S>, ex: &Executor) -> Result<()>
where
    S: vmm_vhost::Backend,
{
    let h = SafeDescriptor::try_from(&backend_server as &dyn AsRawDescriptor)
        .map(AsyncWrapper::new)
        .context("failed to get safe descriptor for handler")?;
    let handler_source = ex
        .async_from(h)
        .context("failed to create an async source")?;

    loop {
        handler_source
            .wait_readable()
            .await
            .context("failed to wait for the handler to become readable")?;
        let (hdr, files) = match backend_server.recv_header() {
            Ok((hdr, files)) => (hdr, files),
            Err(VhostError::ClientExit) => {
                info!("vhost-user connection closed");
                // Exit as the client closed the connection.
                return Ok(());
            }
            Err(e) => {
                return Err(e.into());
            }
        };

        if backend_server.needs_wait_for_payload(&hdr) {
            handler_source
                .wait_readable()
                .await
                .context("failed to wait for the handler to become readable")?;
        }
        backend_server.process_message(hdr, files)?;
    }
}

#[cfg(test)]
pub mod test_helpers {
    use std::os::unix::net::UnixStream;

    use tempfile::TempDir;
    use vmm_vhost::connection::Listener;
    use vmm_vhost::unix::SocketListener;
    use vmm_vhost::BackendServer;

    pub(crate) fn setup() -> (SocketListener, TempDir) {
        let dir = tempfile::Builder::new()
            .prefix("/tmp/vhost_test")
            .tempdir()
            .unwrap();
        let mut path = dir.path().to_owned();
        path.push("sock");
        let listener = SocketListener::new(&path, true).unwrap();

        (listener, dir)
    }

    pub(crate) fn connect(dir: tempfile::TempDir) -> UnixStream {
        let mut path = dir.path().to_owned();
        path.push("sock");
        UnixStream::connect(path).unwrap()
    }

    pub(crate) fn listen<S: vmm_vhost::Backend>(
        mut listener: SocketListener,
        handler: S,
    ) -> BackendServer<S> {
        let connection = listener.accept().unwrap().unwrap();
        BackendServer::new(connection, handler)
    }
}