Files
galaxy/app/src/terminal/wsl/model.rs
T

119 lines
4.2 KiB
Rust

use itertools::Itertools as _;
use windows_registry::CURRENT_USER;
use windows_result::Error as WindowsError;
use crate::send_telemetry_from_ctx;
use crate::server::telemetry::TelemetryEvent;
const DOCKER_DESKTOP_WSL_DISTRO_PREFIX: &str = "docker-desktop";
const RANCHER_DESKTOP_WSL_DISTRO_PREFIX: &str = "rancher-desktop";
/// Contains information about WSL distributions available on the user's Windows machine.
pub(crate) struct WslInfo {
distributions: Vec<Distribution>,
}
impl Entity for WslInfo {
type Event = ();
}
impl SingletonEntity for WslInfo {}
#[derive(Debug, PartialEq)]
pub(crate) struct Distribution {
uuid: String,
pub name: String,
pub is_default: bool,
}
impl WslInfo {
pub(crate) fn new(ctx: &mut ModelContext<Self>) -> Self {
let distributions = Self::find_available_distributions()
.inspect_err(|err| match err {
// This error merely occurs when user doesn't have WSL installed/enabled.
Error::MainKey(err) => {
log::info!("{err:#}");
send_telemetry_from_ctx!(TelemetryEvent::WSLRegistryError, ctx);
}
_ => {
log::error!("{err:#}");
}
})
.unwrap_or_default();
Self { distributions }
}
pub(crate) fn distributions(&self) -> impl Iterator<Item = &Distribution> {
self.distributions.iter()
}
/// Finds available WSL distributions available on the local machine.
fn find_available_distributions() -> Result<Vec<Distribution>, Error> {
// The storage format is the following:
// Lxss/
// DefaultDistribution: 1787c461-d291-401d-b579-1ceff55f97e3
// {1787c461-d291-401d-b579-1ceff55f97e3}
// DistributionName: Ubuntu
// {63375eca-1f0a-4d5d-9682-16b0d42fded8}
// DistributionName: Ubuntu-18.04
let key = CURRENT_USER
.open("Software\\Microsoft\\Windows\\CurrentVersion\\Lxss")
.map_err(Error::MainKey)?;
let default_distribution_uuid = key
.get_string("DefaultDistribution")
.inspect_err(|err| log::warn!("Could not obtain the default distribution: {err:#}"));
let distribution_keys = key.keys().map_err(Error::DistributionIterator)?;
let distributions = distribution_keys
.into_iter()
.flat_map(|uuid| {
let distribution_key = key
.open(&uuid)
.inspect_err(|err| {
log::error!("Could not open distribution registry key: {err:#}")
})
.ok()?;
let name = distribution_key
.get_string("DistributionName")
.inspect_err(|err| {
// Some entries don't have names, that's not an error state we need to monitor.
if err.code() != KEY_NOT_FOUND_ERR {
log::error!("Unable to read distribution name: {err:#}");
}
})
.ok()?;
// Docker uses WSL on windows but we don't expect users to start a new session for
// that WSL instance. Same with Rancher.
if name.starts_with(DOCKER_DESKTOP_WSL_DISTRO_PREFIX)
|| name.starts_with(RANCHER_DESKTOP_WSL_DISTRO_PREFIX)
{
return None;
}
Some(Distribution {
name,
is_default: default_distribution_uuid
.as_ref()
.is_ok_and(|default_distribution_uuid| *default_distribution_uuid == uuid),
uuid,
})
})
.collect_vec();
if distributions.iter().all(|distro| !distro.is_default) {
log::warn!("No distribution matched the default guid");
}
Ok(distributions)
}
}
#[derive(thiserror::Error, Debug)]
enum Error {
#[error("Error opening the main key: {0:#}")]
MainKey(#[source] WindowsError),
#[error("Could not iterate through distributions: {0:#}")]
DistributionIterator(#[source] WindowsError),
}