diff --git a/cli/src/tunnels.rs b/cli/src/tunnels.rs index 801b6545e51..5d97b757afc 100644 --- a/cli/src/tunnels.rs +++ b/cli/src/tunnels.rs @@ -12,6 +12,7 @@ pub mod shutdown_signal; pub mod singleton_client; pub mod singleton_server; +mod wsl_detect; mod challenge; mod control_server; mod nosleep; diff --git a/cli/src/tunnels/dev_tunnels.rs b/cli/src/tunnels/dev_tunnels.rs index fa8d6cbbf20..8476028a2f5 100644 --- a/cli/src/tunnels/dev_tunnels.rs +++ b/cli/src/tunnels/dev_tunnels.rs @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ use crate::auth; use crate::constants::{ - CONTROL_PORT, IS_INTERACTIVE_CLI, PROTOCOL_VERSION_TAG, PROTOCOL_VERSION_TAG_PREFIX, - TUNNEL_SERVICE_USER_AGENT, + CONTROL_PORT, IS_INTERACTIVE_CLI, PROTOCOL_VERSION_TAG, TUNNEL_SERVICE_USER_AGENT, }; use crate::state::{LauncherPaths, PersistedState}; use crate::util::errors::{ @@ -32,6 +31,8 @@ use tunnels::management::{ NO_REQUEST_OPTIONS, }; +use super::wsl_detect::is_wsl_installed; + #[derive(Clone, Serialize, Deserialize)] pub struct PersistedTunnel { pub name: String, @@ -304,7 +305,7 @@ impl DevTunnels { return Ok((full_tunnel, persisted)); } - full_tunnel.tags = vec![name.to_string(), VSCODE_CLI_TUNNEL_TAG.to_string()]; + full_tunnel.tags = self.get_tags(&name); let new_tunnel = spanf!( self.log, @@ -383,11 +384,9 @@ impl DevTunnels { } }; - if !tunnel.tags.iter().any(|t| t == PROTOCOL_VERSION_TAG) { - tunnel = self - .update_protocol_version_tag(tunnel, &HOST_TUNNEL_REQUEST_OPTIONS) - .await?; - } + tunnel = self + .sync_tunnel_tags(&persisted.name, tunnel, &HOST_TUNNEL_REQUEST_OPTIONS) + .await?; let locator = TunnelLocator::try_from(&tunnel).unwrap(); let host_token = get_host_token_from_tunnel(&tunnel); @@ -504,23 +503,40 @@ impl DevTunnels { } } + /// Gets the expected tunnel tags + fn get_tags(&self, name: &str) -> Vec { + let mut tags = vec![ + name.to_string(), + PROTOCOL_VERSION_TAG.to_string(), + VSCODE_CLI_TUNNEL_TAG.to_string(), + ]; + + if is_wsl_installed(&self.log) { + tags.push("_wsl".to_string()) + } + + tags + } + /// Ensures the tunnel contains a tag for the current PROTCOL_VERSION, and no /// other version tags. - async fn update_protocol_version_tag( + async fn sync_tunnel_tags( &self, + name: &str, tunnel: Tunnel, options: &TunnelRequestOptions, ) -> Result { + let new_tags = self.get_tags(name); + if vec_eq_unsorted(&tunnel.tags, &new_tags) { + return Ok(tunnel); + } + debug!( self.log, - "Updating tunnel protocol version tag to {}", PROTOCOL_VERSION_TAG + "Updating tunnel tags {} -> {}", + tunnel.tags.join(", "), + new_tags.join(", ") ); - let mut new_tags: Vec = tunnel - .tags - .into_iter() - .filter(|t| !t.starts_with(PROTOCOL_VERSION_TAG_PREFIX)) - .collect(); - new_tags.push(PROTOCOL_VERSION_TAG.to_string()); let tunnel_update = Tunnel { tags: new_tags, @@ -982,6 +998,20 @@ fn clean_hostname_for_tunnel(hostname: &str) -> String { } } +fn vec_eq_unsorted(a: &[String], b: &[String]) -> bool { + if a.len() != b.len() { + return false; + } + + for item in a { + if !b.contains(item) { + return false; + } + } + + true +} + #[cfg(test)] mod test { use super::*; diff --git a/cli/src/tunnels/wsl_detect.rs b/cli/src/tunnels/wsl_detect.rs new file mode 100644 index 00000000000..ec386feb26e --- /dev/null +++ b/cli/src/tunnels/wsl_detect.rs @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +use crate::log; + +#[cfg(not(windows))] +pub fn is_wsl_installed(_log: &log::Logger) -> bool { + false +} + +#[cfg(windows)] +pub fn is_wsl_installed(log: &log::Logger) -> bool { + use std::{path::PathBuf, process::Command}; + + let system32 = { + let sys_root = match std::env::var("SystemRoot") { + Ok(s) => s, + Err(_) => return false, + }; + + let is_32_on_64 = std::env::var("PROCESSOR_ARCHITEW6432").is_ok(); + let mut system32 = PathBuf::from(sys_root); + system32.push(if is_32_on_64 { "Sysnative" } else { "System32" }); + system32 + }; + + // Windows builds < 22000 + let mut maybe_lxss = system32.join("lxss"); + maybe_lxss.push("LxssManager.dll"); + if maybe_lxss.exists() { + trace!(log, "wsl availability detected via lxss"); + return true; + } + + // Windows builds >= 22000 + let maybe_wsl = system32.join("wsl.exe"); + if maybe_wsl.exists() { + if let Ok(s) = Command::new(maybe_wsl).arg("--status").output() { + if s.status.success() { + trace!(log, "wsl availability detected via subprocess"); + return true; + } + } + } + + trace!(log, "wsl not detected"); + + false +}