fn run_server_process()

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),
        }
    })
}