mirror of
https://github.com/aphrody-labs/WSL.git
synced 2026-10-18 13:15:21 +00:00
Use AF_UNIX for OpenVMM RPC connections
This commit is contained in:
1 parent
180e104a38
commit
eb59238333
8 files changed
+77
-53
No files matched your search
@@ -26,7 +26,7 @@ add_custom_command(
|
||||
OUTPUT ${WSLOPENVMM_DLL}
|
||||
COMMAND ${CMAKE_COMMAND} -E env "VM_SERVICE_PROTO=${VM_SERVICE_PROTO}" ${CARGO_EXECUTABLE} build --manifest-path ${CMAKE_CURRENT_LIST_DIR}/Cargo.toml --target ${RUST_TARGET} --target-dir ${CARGO_TARGET_DIR} ${CARGO_BUILD_ARGUMENTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CARGO_OUTPUT} ${WSLOPENVMM_DLL}
|
||||
DEPENDS ${VM_SERVICE_PROTO} Cargo.lock Cargo.toml build.rs src/client.rs src/lib.rs src/named_pipe.rs wslopenvmm.h
|
||||
DEPENDS ${VM_SERVICE_PROTO} Cargo.lock Cargo.toml build.rs src/client.rs src/lib.rs src/af_unix.rs wslopenvmm.h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
VERBATIM)
|
||||
|
||||
|
||||
Generated
+1
@@ -1149,6 +1149,7 @@ dependencies = [
|
||||
"prost",
|
||||
"prost-types",
|
||||
"protoc-bin-vendored",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tonic",
|
||||
|
||||
@@ -11,6 +11,7 @@ crate-type = ["cdylib"]
|
||||
[dependencies]
|
||||
hyper-util = { version = "0.1", features = ["tokio"] }
|
||||
parking_lot = "0.12"
|
||||
socket2 = "0.6"
|
||||
prost = "0.14"
|
||||
prost-types = "0.14"
|
||||
tonic = "0.14"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use hyper_util::rt::TokioIo;
|
||||
use socket2::{Domain, SockAddr, Socket, Type};
|
||||
use tokio::net::TcpStream;
|
||||
use tonic::transport::{Channel, Endpoint};
|
||||
use tower::service_fn;
|
||||
|
||||
/// Connects a Tonic channel to the OpenVMM AF_UNIX listener.
|
||||
pub async fn connect_channel(
|
||||
socket_path: PathBuf,
|
||||
timeout: Duration,
|
||||
) -> Result<Channel, tonic::transport::Error> {
|
||||
Endpoint::from_static("http://[::]:50051")
|
||||
.connect_timeout(timeout)
|
||||
.connect_with_connector(service_fn(move |_| {
|
||||
let socket_path = socket_path.clone();
|
||||
async move { open_socket(&socket_path, timeout).await.map(TokioIo::new) }
|
||||
}))
|
||||
.await
|
||||
}
|
||||
|
||||
async fn open_socket(path: &Path, timeout: Duration) -> io::Result<TcpStream> {
|
||||
let address = SockAddr::unix(path)?;
|
||||
tokio::time::timeout(timeout, async {
|
||||
loop {
|
||||
match connect_socket(&address).await {
|
||||
Ok(stream) => return Ok(stream),
|
||||
Err(error)
|
||||
if matches!(
|
||||
error.kind(),
|
||||
io::ErrorKind::NotFound | io::ErrorKind::ConnectionRefused
|
||||
) => {}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::TimedOut, "timed out connecting to OpenVMM"))?
|
||||
}
|
||||
|
||||
/// Registers the Winsock AF_UNIX socket with Tokio's Windows socket reactor.
|
||||
/// The socket remains AF_UNIX; TCP-specific options and address queries must
|
||||
/// not be used on the returned stream.
|
||||
async fn connect_socket(address: &SockAddr) -> io::Result<TcpStream> {
|
||||
let socket = Socket::new(Domain::UNIX, Type::STREAM, None)?;
|
||||
socket.set_nonblocking(true)?;
|
||||
match socket.connect(address) {
|
||||
Ok(()) => {}
|
||||
Err(error) if error.kind() == io::ErrorKind::WouldBlock => {}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
|
||||
let stream = TcpStream::from_std(socket.into())?;
|
||||
stream.writable().await?;
|
||||
if let Some(error) = stream.take_error()? {
|
||||
return Err(error);
|
||||
}
|
||||
Ok(stream)
|
||||
}
|
||||
@@ -13,7 +13,7 @@ use windows::Win32::Foundation::{E_FAIL, E_INVALIDARG, ERROR_ALREADY_EXISTS, ERR
|
||||
use windows::core::{GUID, HRESULT};
|
||||
use windows_sys::Win32::Networking::WinSock::{AF_INET, AF_INET6};
|
||||
|
||||
use crate::{named_pipe, vmservice};
|
||||
use crate::{af_unix, vmservice};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -40,7 +40,7 @@ impl VmConfigBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_vm(&self, pipe_name: String, timeout_ms: u32) -> Result<VmHandle, HRESULT> {
|
||||
pub fn create_vm(&self, socket_path: String, timeout_ms: u32) -> Result<VmHandle, HRESULT> {
|
||||
let runtime = match tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
@@ -49,7 +49,7 @@ impl VmConfigBuilder {
|
||||
Err(_) => return Err(E_FAIL),
|
||||
};
|
||||
let timeout = Duration::from_millis(u64::from(timeout_ms));
|
||||
let channel = match runtime.block_on(named_pipe::connect_channel(pipe_name, timeout)) {
|
||||
let channel = match runtime.block_on(af_unix::connect_channel(socket_path.into(), timeout)) {
|
||||
Ok(channel) => channel,
|
||||
Err(error) if error.to_string().contains("timed out") => {
|
||||
return Err(HRESULT::from_win32(WAIT_TIMEOUT.0));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#![expect(clippy::missing_safety_doc)]
|
||||
|
||||
mod client;
|
||||
pub mod named_pipe;
|
||||
pub mod af_unix;
|
||||
|
||||
pub mod vmservice {
|
||||
tonic::include_proto!("vmservice");
|
||||
@@ -200,7 +200,7 @@ pub unsafe extern "C" fn WslOpenVmmConfigSetVirtioConsolePath(
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn WslOpenVmmCreateVm(
|
||||
config: *mut *mut WslOpenVmmConfig,
|
||||
pipe_name: *const u16,
|
||||
socket_path: *const u16,
|
||||
timeout_ms: u32,
|
||||
vm: *mut *mut WslOpenVmmVm,
|
||||
) -> i32 {
|
||||
@@ -215,13 +215,13 @@ pub unsafe extern "C" fn WslOpenVmmCreateVm(
|
||||
if config_handle.is_null() {
|
||||
return E_POINTER.0;
|
||||
}
|
||||
let pipe_name = match unsafe { string_from_wide(pipe_name) } {
|
||||
Ok(pipe_name) => pipe_name,
|
||||
let socket_path = match unsafe { string_from_wide(socket_path) } {
|
||||
Ok(socket_path) => socket_path,
|
||||
Err(error) => return error.0,
|
||||
};
|
||||
|
||||
let config_box = unsafe { Box::from_raw(config_handle) };
|
||||
let result = config_box.0.lock().create_vm(pipe_name, timeout_ms);
|
||||
let result = config_box.0.lock().create_vm(socket_path, timeout_ms);
|
||||
let client = match result {
|
||||
Ok(client) => client,
|
||||
Err(error) => {
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
use std::io;
|
||||
use std::time::Duration;
|
||||
|
||||
use hyper_util::rt::TokioIo;
|
||||
use tokio::net::windows::named_pipe::{ClientOptions, NamedPipeClient};
|
||||
use tonic::transport::{Channel, Endpoint};
|
||||
use tower::service_fn;
|
||||
use windows::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_PIPE_BUSY};
|
||||
|
||||
/// Connects a Tonic channel to the OpenVMM named-pipe listener.
|
||||
pub async fn connect_channel(
|
||||
pipe_name: String,
|
||||
timeout: Duration,
|
||||
) -> Result<Channel, tonic::transport::Error> {
|
||||
let endpoint = Endpoint::from_static("http://[::]:50051").connect_timeout(timeout);
|
||||
endpoint
|
||||
.connect_with_connector(service_fn(move |_| {
|
||||
let pipe_name = pipe_name.clone();
|
||||
async move {
|
||||
open_pipe(&pipe_name, timeout).await.map(TokioIo::new)
|
||||
}
|
||||
}))
|
||||
.await
|
||||
}
|
||||
|
||||
async fn open_pipe(pipe_name: &str, timeout: Duration) -> io::Result<NamedPipeClient> {
|
||||
tokio::time::timeout(timeout, async {
|
||||
loop {
|
||||
match ClientOptions::new().open(pipe_name) {
|
||||
Ok(pipe) => return Ok(pipe),
|
||||
Err(error)
|
||||
if matches!(error.raw_os_error(), Some(code)
|
||||
if code == ERROR_FILE_NOT_FOUND.0 as i32 || code == ERROR_PIPE_BUSY.0 as i32) => {}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::TimedOut, "timed out connecting to OpenVMM"))?
|
||||
}
|
||||
@@ -26,7 +26,7 @@ __declspec(dllimport) HRESULT WslOpenVmmConfigSetConsommeNic(_Inout_ WslOpenVmmC
|
||||
__declspec(dllimport) HRESULT WslOpenVmmConfigAddSerialPort(_Inout_ WslOpenVmmConfig* Config, _In_ UINT32 Port, _In_ LPCWSTR PipeName);
|
||||
__declspec(dllimport) HRESULT WslOpenVmmConfigSetVirtioConsolePath(_Inout_ WslOpenVmmConfig* Config, _In_ LPCWSTR Path);
|
||||
// On success, this consumes Config and sets it to nullptr. On failure, Config remains valid and Vm is nullptr.
|
||||
__declspec(dllimport) HRESULT WslOpenVmmCreateVm(_Inout_ WslOpenVmmConfig** Config, _In_ LPCWSTR PipeName, _In_ UINT32 TimeoutMs, _Out_ WslOpenVmmVm** Vm);
|
||||
__declspec(dllimport) HRESULT WslOpenVmmCreateVm(_Inout_ WslOpenVmmConfig** Config, _In_ LPCWSTR SocketPath, _In_ UINT32 TimeoutMs, _Out_ WslOpenVmmVm** Vm);
|
||||
|
||||
__declspec(dllimport) void WslOpenVmmDestroyVm(_In_opt_ WslOpenVmmVm* Vm);
|
||||
__declspec(dllimport) HRESULT WslOpenVmmVmResume(_Inout_ WslOpenVmmVm* Vm);
|
||||
|
||||
Reference in new issue
Block a user