in src/commands.rs [177:300]
fn run_server_process(startup_timeout: Option<Duration>) -> Result<ServerStartup> {
use futures::StreamExt;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use tokio::net::windows::named_pipe;
use uuid::Uuid;
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::System::Threading::{
CreateProcessW, CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW, CREATE_UNICODE_ENVIRONMENT,
PROCESS_INFORMATION, STARTUPINFOW,
};
trace!("run_server_process");
// Create a mini event loop and register our named pipe server
let runtime = Runtime::new()?;
let pipe_name = &format!(r"\\.\pipe\{}", Uuid::new_v4().as_simple());
// Spawn a server which should come back and connect to us
let exe_path = env::current_exe()?;
let mut exe = OsStr::new(&exe_path)
.encode_wide()
.chain(Some(0u16))
.collect::<Vec<u16>>();
let mut envp = {
let mut v = vec![];
let extra_vars = vec![
(OsString::from("SCCACHE_START_SERVER"), OsString::from("1")),
(
OsString::from("SCCACHE_STARTUP_NOTIFY"),
OsString::from(&pipe_name),
),
(OsString::from("RUST_BACKTRACE"), OsString::from("1")),
];
for (key, val) in env::vars_os().chain(extra_vars) {
v.extend(
key.encode_wide()
.chain(Some('=' as u16))
.chain(val.encode_wide())
.chain(Some(0)),
);
}
v.push(0);
v
};
let workdir = exe_path
.parent()
.expect("executable path has no parent?!")
.as_os_str()
.encode_wide()
.chain(Some(0u16))
.collect::<Vec<u16>>();
// TODO: Expose `bInheritHandles` argument of `CreateProcessW` through the
// standard library's `Command` type and then use that instead.
let mut pi = PROCESS_INFORMATION {
hProcess: 0,
hThread: 0,
dwProcessId: 0,
dwThreadId: 0,
};
let mut si: STARTUPINFOW = unsafe { mem::zeroed() };
si.cb = mem::size_of::<STARTUPINFOW>() as _;
if unsafe {
CreateProcessW(
exe.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW,
envp.as_mut_ptr().cast(),
workdir.as_ptr(),
&si,
&mut pi,
) != 0
} {
unsafe {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
} else {
return Err(io::Error::last_os_error().into());
}
fn create_named_pipe(
pipe_name: &str,
is_first: bool,
) -> io::Result<named_pipe::NamedPipeServer> {
named_pipe::ServerOptions::new()
.first_pipe_instance(is_first)
.reject_remote_clients(true)
.access_inbound(true)
.access_outbound(true)
.in_buffer_size(65536)
.out_buffer_size(65536)
.create(pipe_name)
}
let startup = async move {
let pipe = create_named_pipe(pipe_name, true)?;
let incoming = futures::stream::try_unfold(pipe, |listener| async move {
listener.connect().await?;
let new_listener = create_named_pipe(pipe_name, false)?;
Ok::<_, io::Error>(Some((listener, new_listener)))
});
futures::pin_mut!(incoming);
let socket = incoming.next().await;
let socket = socket.unwrap(); // incoming() never returns None
read_server_startup_status(socket?).await
};
let timeout = startup_timeout.unwrap_or(SERVER_STARTUP_TIMEOUT);
runtime.block_on(async move {
match tokio::time::timeout(timeout, startup).await {
Ok(result) => result,
Err(_elapsed) => Ok(ServerStartup::TimedOut),
}
})
}