From 230e111bb3d8dfb9704f2d66492254bb21323570 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Thu, 19 Mar 2020 17:52:36 +0100 Subject: ipc: Inherit a TCP socket via env var on Windows --- Cargo.lock | 12 ++++++ ipc/Cargo.toml | 4 ++ ipc/src/lib.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++---------- store/src/lib.rs | 6 +-- 4 files changed, 107 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa1e93e9..93900a13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,6 +449,15 @@ dependencies = [ "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ctor" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "diff" version = "0.1.12" @@ -1615,6 +1624,7 @@ dependencies = [ "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "capnp-rpc 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ctor 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1629,6 +1639,7 @@ dependencies = [ "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2318,6 +2329,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum crossterm_winapi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02de3a35bcabb5bb6dc7d4449abb546d23f123b06f074e2cf1c50db516da6ac8" "checksum csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" "checksum csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +"checksum ctor 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "47c5e5ac752e18207b12e16b10631ae5f7f68f8805f335f9b817ead83d9ffce1" "checksum diff 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index 745e4d9d..8e315bc0 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -37,6 +37,10 @@ tokio = "0.1" tokio-core = "0.1" tokio-io = "0.1.4" +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", default-features = false, features = ["winsock2"] } +ctor = "0.1" + [build-dependencies] lalrpop = "0.17" diff --git a/ipc/src/lib.rs b/ipc/src/lib.rs index 623aa66d..028a5052 100644 --- a/ipc/src/lib.rs +++ b/ipc/src/lib.rs @@ -41,7 +41,7 @@ use std::io::{self, Read, Write}; use std::net::{Ipv4Addr, SocketAddr, TcpStream, TcpListener}; use std::path::PathBuf; -use anyhow::Result; +use anyhow::{anyhow, Result}; use fs2::FileExt; use futures::{Future, Stream}; @@ -52,11 +52,12 @@ use tokio_io::AsyncRead; use capnp_rpc::{RpcSystem, twoparty}; use capnp_rpc::rpc_twoparty_capnp::Side; -/* Unix-specific options. */ -use std::os::unix::io::{IntoRawFd, FromRawFd}; -use std::os::unix::fs::OpenOptionsExt; - -/* XXX: Implement Windows support. */ +#[cfg(unix)] +use std::os::unix::{io::{IntoRawFd, FromRawFd}, fs::OpenOptionsExt}; +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, IntoRawSocket, FromRawSocket}; +#[cfg(windows)] +use winapi::um::winsock2; use std::process::{Command, Stdio}; use std::thread; @@ -139,12 +140,14 @@ impl Descriptor { }; fs::create_dir_all(self.ctx.home())?; - let mut file = fs::OpenOptions::new() + let mut file = fs::OpenOptions::new(); + file .read(true) .write(true) - .create(true) - .mode(0o600) - .open(&self.rendezvous)?; + .create(true); + #[cfg(unix)] + file.mode(0o600); + let mut file = file.open(&self.rendezvous)?; file.lock_exclusive()?; let mut c = vec![]; @@ -205,17 +208,44 @@ impl Descriptor { } fn fork(&self, listener: TcpListener) -> Result<()> { - Command::new(&self.executable) + let mut cmd = Command::new(&self.executable); + cmd .arg("--home") .arg(self.ctx.home()) .arg("--lib") .arg(self.ctx.lib()) .arg("--ephemeral") .arg(self.ctx.ephemeral().to_string()) - .stdin(unsafe { Stdio::from_raw_fd(listener.into_raw_fd()) }) .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn()?; + .stderr(Stdio::null()); + + #[cfg(unix)] + { + // Pass the listening TCP socket as child stdin. + cmd.stdin(unsafe { Stdio::from_raw_fd(listener.into_raw_fd()) }); + } + #[cfg(windows)] + { + // Sockets for `TcpListener` are not inheritable by default, so + // let's make them so, since we'll pass them to a child process. + unsafe { + match winapi::um::handleapi::SetHandleInformation( + listener.as_raw_socket() as _, + winapi::um::winbase::HANDLE_FLAG_INHERIT, + winapi::um::winbase::HANDLE_FLAG_INHERIT, + ) { + 0 => Err(std::io::Error::last_os_error()), + _ => Ok(()) + }? + }; + // We can't pass the socket to stdin directly on Windows, since only + // non-overlapped (blocking) I/O handles can be redirected there. + // We use Tokio (async I/O), so we just pass it via env var rather than + // establishing a whole separate channel to pass the socket through. + cmd.env("SOCKET", format!("{}", listener.into_raw_socket())); + } + + cmd.spawn()?; Ok(()) } @@ -253,7 +283,7 @@ impl Server { if args.len() != 7 || args[1] != "--home" || args[3] != "--lib" || args[5] != "--ephemeral" { - return Err(anyhow::anyhow!( + return Err(anyhow!( "Usage: {} --home --lib \ --ephemeral true|false", args[0])); } @@ -266,7 +296,7 @@ impl Server { cfg.set_ephemeral(); } } else { - return Err(anyhow::anyhow!( + return Err(anyhow!( "Expected 'true' or 'false' for --ephemeral, got: {}", args[6])); } @@ -276,8 +306,11 @@ impl Server { /// Turns this process into a server. /// - /// External servers must call this early on. Expects 'stdin' to - /// be a listening TCP socket. + /// External servers must call this early on. + /// + /// On Linux expects 'stdin' to be a listening TCP socket. + /// On Windows this expects `SOCKET` env var to be set to a listening socket + /// of the Windows Sockets API `SOCKET` value. /// /// # Example /// @@ -299,7 +332,18 @@ impl Server { /// } /// ``` pub fn serve(&mut self) -> Result<()> { - self.serve_listener(unsafe { TcpListener::from_raw_fd(0) }) + #[cfg(unix)] + fn fetch_listener() -> Result { + Ok(unsafe { TcpListener::from_raw_fd(0) }) + } + #[cfg(windows)] + fn fetch_listener() -> Result { + let socket = std::env::var("SOCKET")?.parse()?; + + Ok(unsafe { TcpListener::from_raw_socket(socket) }) + } + + self.serve_listener(fetch_listener()?) } fn serve_listener(&mut self, l: TcpListener) -> Result<()> { @@ -427,3 +471,30 @@ pub enum Error { #[error("Connection closed unexpectedly.")] ConnectionClosed(Vec), } + +// Global initialization and cleanup of the Windows Sockets API (WSA) module. +// NOTE: This has to be top-level in order for `ctor::{ctor, dtor}` to work. +#[cfg(windows)] +use std::sync::atomic::{AtomicBool, Ordering}; +#[cfg(windows)] +static WSA_INITED: AtomicBool = AtomicBool::new(false); + +#[cfg(windows)] +#[ctor::ctor] +fn wsa_startup() { + unsafe { + let ret = winsock2::WSAStartup( + 0x202, // version 2.2 + &mut std::mem::zeroed(), + ); + WSA_INITED.store(ret != 0, Ordering::SeqCst); + } +} + +#[cfg(windows)] +#[ctor::dtor] +fn wsa_cleanup() { + if WSA_INITED.load(Ordering::SeqCst) { + let _ = unsafe { winsock2::WSACleanup() }; + } +} diff --git a/store/src/lib.rs b/store/src/lib.rs index 1156a5ad..dc7cb9a5 100644 --- a/store/src/lib.rs +++ b/store/src/lib.rs @@ -123,11 +123,7 @@ impl Store { let core = Core::new()?; let handle = core.handle(); - let mut rpc_system - = match descriptor.connect(&handle) { - Ok(r) => r, - Err(e) => return Err(e.into()), - }; + let mut rpc_system = descriptor.connect(&handle)?; let client: node::Client = rpc_system.bootstrap(Side::Server); handle.spawn(rpc_system.map_err(|_e| ())); -- cgit v1.2.3