diff --git a/crates/remote/src/transport/wsl.rs b/crates/remote/src/transport/wsl.rs index 9c0cba8a3acac10087210942e9c5bb800a49906d..6745c4409a0acf39b86999335f7d0826fc978e09 100644 --- a/crates/remote/src/transport/wsl.rs +++ b/crates/remote/src/transport/wsl.rs @@ -38,12 +38,14 @@ impl From for WslConnectionOptions { } } +#[derive(Debug)] pub(crate) struct WslRemoteConnection { remote_binary_path: Option>, platform: RemotePlatform, shell: String, default_system_shell: String, connection_options: WslConnectionOptions, + can_exec: bool, } impl WslRemoteConnection { @@ -71,18 +73,48 @@ impl WslRemoteConnection { platform: RemotePlatform { os: "", arch: "" }, shell: String::new(), default_system_shell: String::from("/bin/sh"), + can_exec: true, }; delegate.set_status(Some("Detecting WSL environment"), cx); + this.can_exec = this.detect_can_exec().await?; this.platform = this.detect_platform().await?; this.shell = this.detect_shell().await?; this.remote_binary_path = Some( this.ensure_server_binary(&delegate, release_channel, version, commit, cx) .await?, ); + log::debug!("Detected WSL environment: {this:#?}"); Ok(this) } + async fn detect_can_exec(&self) -> Result { + let options = &self.connection_options; + let program = "uname"; + let args = &["-m"]; + let output = wsl_command_impl(options, program, args, true) + .output() + .await?; + + if !output.status.success() { + let output = wsl_command_impl(options, program, args, false) + .output() + .await?; + + if !output.status.success() { + return Err(anyhow!( + "Command '{}' failed: {}", + program, + String::from_utf8_lossy(&output.stderr).trim() + )); + } + + Ok(false) + } else { + Ok(true) + } + } + async fn detect_platform(&self) -> Result { let arch_str = self.run_wsl_command("uname", &["-m"]).await?; let arch_str = arch_str.trim().to_string(); @@ -103,15 +135,15 @@ impl WslRemoteConnection { } async fn windows_path_to_wsl_path(&self, source: &Path) -> Result { - windows_path_to_wsl_path_impl(&self.connection_options, source).await + windows_path_to_wsl_path_impl(&self.connection_options, source, self.can_exec).await } fn wsl_command(&self, program: &str, args: &[impl AsRef]) -> process::Command { - wsl_command_impl(&self.connection_options, program, args) + wsl_command_impl(&self.connection_options, program, args, self.can_exec) } async fn run_wsl_command(&self, program: &str, args: &[&str]) -> Result { - run_wsl_command_impl(&self.connection_options, program, args).await + run_wsl_command_impl(&self.connection_options, program, args, self.can_exec).await } async fn ensure_server_binary( @@ -296,7 +328,10 @@ impl RemoteConnection for WslRemoteConnection { let mut proxy_args = vec![]; for env_var in ["RUST_LOG", "RUST_BACKTRACE", "ZED_GENERATE_MINIDUMPS"] { if let Some(value) = std::env::var(env_var).ok() { - proxy_args.push(format!("{}='{}'", env_var, value)); + // We don't quote the value here as it seems excessive and may result in invalid envs for the + // proxy server. For example, `RUST_LOG='debug'` will result in a warning "invalid logging spec 'debug'', ignoring it" + // in the proxy server. Therefore, we pass the env vars as is. + proxy_args.push(format!("{}={}", env_var, value)); } } proxy_args.push(remote_binary_path.display(PathStyle::Posix).into_owned()); @@ -335,19 +370,25 @@ impl RemoteConnection for WslRemoteConnection { ) -> Task> { cx.background_spawn({ let options = self.connection_options.clone(); + let can_exec = self.can_exec; async move { - let wsl_src = windows_path_to_wsl_path_impl(&options, &src_path).await?; - - run_wsl_command_impl(&options, "cp", &["-r", &wsl_src, &dest_path.to_string()]) - .await - .map_err(|e| { - anyhow!( - "failed to upload directory {} -> {}: {}", - src_path.display(), - dest_path.to_string(), - e - ) - })?; + let wsl_src = windows_path_to_wsl_path_impl(&options, &src_path, can_exec).await?; + + run_wsl_command_impl( + &options, + "cp", + &["-r", &wsl_src, &dest_path.to_string()], + can_exec, + ) + .await + .map_err(|e| { + anyhow!( + "failed to upload directory {} -> {}: {}", + src_path.display(), + dest_path.to_string(), + e + ) + })?; Ok(()) } @@ -463,17 +504,21 @@ async fn sanitize_path(path: &Path) -> Result { async fn windows_path_to_wsl_path_impl( options: &WslConnectionOptions, source: &Path, + exec: bool, ) -> Result { let source = sanitize_path(source).await?; - run_wsl_command_impl(options, "wslpath", &["-u", &source]).await + run_wsl_command_impl(options, "wslpath", &["-u", &source], exec).await } async fn run_wsl_command_impl( options: &WslConnectionOptions, program: &str, args: &[&str], + exec: bool, ) -> Result { - let output = wsl_command_impl(options, program, args).output().await?; + let output = wsl_command_impl(options, program, args, exec) + .output() + .await?; if !output.status.success() { return Err(anyhow!( @@ -493,6 +538,7 @@ fn wsl_command_impl( options: &WslConnectionOptions, program: &str, args: &[impl AsRef], + exec: bool, ) -> process::Command { let mut command = util::command::new_smol_command("wsl.exe"); @@ -507,10 +553,13 @@ fn wsl_command_impl( .arg("--distribution") .arg(&options.distro_name) .arg("--cd") - .arg("~") - .arg("--exec") - .arg(program) - .args(args); + .arg("~"); + + if exec { + command.arg("--exec"); + } + + command.arg(program).args(args); log::debug!("wsl {:?}", command); command