use std::process::Command;
use log::{debug, trace};
use crate::{matcher::Matcher, Info, Type, Version};
pub fn get() -> Option<Info> {
let release = retrieve()?;
let version = match release.version.as_deref() {
Some("rolling") => Version::Rolling(None),
Some(v) => Version::Custom(v.to_owned()),
None => Version::Unknown,
};
let os_type = match release.distribution.as_ref().map(String::as_ref) {
Some("Amazon") | Some("AmazonAMI") => Type::Amazon,
Some("Arch") => Type::Arch,
Some("CentOS") => Type::CentOS,
Some("Debian") => Type::Debian,
Some("EndeavourOS") => Type::EndeavourOS,
Some("Fedora") | Some("Fedora Linux") => Type::Fedora,
Some("Garuda") => Type::Garuda,
Some("Gentoo") => Type::Gentoo,
Some("Linuxmint") => Type::Mint,
Some("ManjaroLinux") => Type::Manjaro,
Some("Mariner") => Type::Mariner,
Some("NixOS") => Type::NixOS,
Some("openSUSE") => Type::openSUSE,
Some("OracleServer") => Type::OracleLinux,
Some("Pop") => Type::Pop,
Some("Raspbian") => Type::Raspbian,
Some("RedHatEnterprise") | Some("RedHatEnterpriseServer") => Type::RedHatEnterprise,
Some("Solus") => Type::Solus,
Some("SUSE") => Type::SUSE,
Some("Ubuntu") => Type::Ubuntu,
_ => Type::Linux,
};
Some(Info {
os_type,
version,
codename: release.codename,
..Default::default()
})
}
struct LsbRelease {
pub distribution: Option<String>,
pub version: Option<String>,
pub codename: Option<String>,
}
fn retrieve() -> Option<LsbRelease> {
match Command::new("lsb_release").arg("-a").output() {
Ok(output) => {
trace!("lsb_release command returned {:?}", output);
Some(parse(&String::from_utf8_lossy(&output.stdout)))
}
Err(e) => {
debug!("lsb_release command failed with {:?}", e);
None
}
}
}
fn parse(output: &str) -> LsbRelease {
trace!("Trying to parse {:?}", output);
let distribution = Matcher::PrefixedWord {
prefix: "Distributor ID:",
}
.find(output);
let codename = Matcher::PrefixedWord {
prefix: "Codename:",
}
.find(output)
.filter(|c| c != "n/a");
let version = Matcher::PrefixedVersion { prefix: "Release:" }.find(output);
trace!(
"Parsed as '{:?}' distribution and '{:?}' version",
distribution,
version
);
LsbRelease {
distribution,
version,
codename,
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn debian() {
let parse_results = parse(file());
assert_eq!(parse_results.distribution, Some("Debian".to_string()));
assert_eq!(parse_results.version, Some("7.8".to_string()));
assert_eq!(parse_results.codename, Some("wheezy".to_string()));
}
#[test]
fn arch() {
let parse_results = parse(arch_file());
assert_eq!(parse_results.distribution, Some("Arch".to_string()));
assert_eq!(parse_results.version, Some("rolling".to_string()));
assert_eq!(parse_results.codename, None);
}
#[test]
fn fedora() {
let parse_results = parse(fedora_file());
assert_eq!(parse_results.distribution, Some("Fedora".to_string()));
assert_eq!(parse_results.version, Some("26".to_string()));
assert_eq!(parse_results.codename, Some("TwentySix".to_string()));
}
#[test]
fn ubuntu() {
let parse_results = parse(ubuntu_file());
assert_eq!(parse_results.distribution, Some("Ubuntu".to_string()));
assert_eq!(parse_results.version, Some("16.04".to_string()));
assert_eq!(parse_results.codename, Some("xenial".to_string()));
}
#[test]
fn mint() {
let parse_results = parse(mint_file());
assert_eq!(parse_results.distribution, Some("Linuxmint".to_string()));
assert_eq!(parse_results.version, Some("20".to_string()));
assert_eq!(parse_results.codename, Some("ulyana".to_string()));
}
#[test]
fn nixos() {
let parse_results = parse(nixos_file());
assert_eq!(parse_results.distribution, Some("NixOS".to_string()));
assert_eq!(
parse_results.version,
Some("21.05pre275822.916ee862e87".to_string())
);
assert_eq!(parse_results.codename, Some("okapi".to_string()));
}
#[test]
fn amazon1() {
let parse_results = parse(amazon1_file());
assert_eq!(parse_results.distribution, Some("AmazonAMI".to_string()));
assert_eq!(parse_results.version, Some("2018.03".to_string()));
assert_eq!(parse_results.codename, None);
}
#[test]
fn amazon2() {
let parse_results = parse(amazon2_file());
assert_eq!(parse_results.distribution, Some("Amazon".to_string()));
assert_eq!(parse_results.version, Some("2".to_string()));
assert_eq!(parse_results.codename, Some("Karoo".to_string()));
}
#[test]
fn redhat_enterprise_8() {
let parse_results = parse(rhel8_file());
assert_eq!(
parse_results.distribution,
Some("RedHatEnterprise".to_string())
);
assert_eq!(parse_results.version, Some("8.1".to_string()));
assert_eq!(parse_results.codename, Some("Ootpa".to_string()));
}
#[test]
fn redhat_enterprise_7() {
let parse_results = parse(rhel7_file());
assert_eq!(
parse_results.distribution,
Some("RedHatEnterpriseServer".to_string())
);
assert_eq!(parse_results.version, Some("7.7".to_string()));
assert_eq!(parse_results.codename, Some("Maipo".to_string()));
}
#[test]
fn redhat_enterprise_6() {
let parse_results = parse(rhel6_file());
assert_eq!(
parse_results.distribution,
Some("RedHatEnterpriseServer".to_string())
);
assert_eq!(parse_results.version, Some("6.10".to_string()));
assert_eq!(parse_results.codename, Some("Santiago".to_string()));
}
#[test]
fn suse_enterprise_15_1() {
let parse_results = parse(suse_enterprise15_1_file());
assert_eq!(parse_results.distribution, Some("SUSE".to_string()));
assert_eq!(parse_results.version, Some("15.1".to_string()));
assert_eq!(parse_results.codename, None);
}
#[test]
fn suse_enterprise_12_5() {
let parse_results = parse(suse_enterprise12_5_file());
assert_eq!(parse_results.distribution, Some("SUSE".to_string()));
assert_eq!(parse_results.version, Some("12.5".to_string()));
assert_eq!(parse_results.codename, None);
}
#[test]
fn open_suse_15_1() {
let parse_results = parse(open_suse_15_1_file());
assert_eq!(parse_results.distribution, Some("openSUSE".to_string()));
assert_eq!(parse_results.version, Some("15.1".to_string()));
assert_eq!(parse_results.codename, None);
}
#[test]
fn oracle_linux_7_5() {
let parse_results = parse(oracle_server_linux_7_5_file());
assert_eq!(parse_results.distribution, Some("OracleServer".to_string()));
assert_eq!(parse_results.version, Some("7.5".to_string()));
assert_eq!(parse_results.codename, None);
}
#[test]
fn oracle_linux_8_1() {
let parse_results = parse(oracle_server_linux_8_1_file());
assert_eq!(parse_results.distribution, Some("OracleServer".to_string()));
assert_eq!(parse_results.version, Some("8.1".to_string()));
assert_eq!(parse_results.codename, None);
}
#[test]
fn pop_os_20_04_lts() {
let parse_results = parse(pop_os_20_04_lts_file());
assert_eq!(parse_results.distribution, Some("Pop".to_string()));
assert_eq!(parse_results.version, Some("20.04".to_string()));
assert_eq!(parse_results.codename, Some("focal".to_string()));
}
#[test]
fn solus_4_1() {
let parse_results = parse(solus_4_1_file());
assert_eq!(parse_results.distribution, Some("Solus".to_string()));
assert_eq!(parse_results.version, Some("4.1".to_string()));
assert_eq!(parse_results.codename, Some("fortitude".to_string()));
}
#[test]
fn manjaro() {
let parse_results = parse(manjaro_19_0_2_file());
assert_eq!(parse_results.distribution, Some("ManjaroLinux".to_string()));
assert_eq!(parse_results.version, Some("19.0.2".to_string()));
assert_eq!(parse_results.codename, None);
}
#[test]
fn mariner() {
let parse_results = parse(mariner_file());
assert_eq!(parse_results.distribution, Some("Mariner".to_string()));
assert_eq!(parse_results.version, Some("2.0.20220210".to_string()));
assert_eq!(parse_results.codename, Some("Mariner".to_string()));
}
#[test]
fn endeavouros() {
let parse_results = parse(endeavouros_file());
assert_eq!(parse_results.distribution, Some("EndeavourOS".to_string()));
assert_eq!(parse_results.version, Some("rolling".to_string()));
assert_eq!(parse_results.codename, None);
}
#[test]
fn raspbian() {
let parse_results = parse(raspberry_os_file());
assert_eq!(parse_results.distribution, Some("Raspbian".to_string()));
assert_eq!(parse_results.version, Some("10".to_string()));
assert_eq!(parse_results.codename, None);
}
fn file() -> &'static str {
"\nDistributor ID: Debian\n\
Description: Debian GNU/Linux 7.8 (wheezy)\n\
Release: 7.8\n\
Codename: wheezy\n\
"
}
fn arch_file() -> &'static str {
"\nLSB Version: 1.4\n\
Distributor ID: Arch\n\
Description: Arch Linux\n\
Release: rolling\n\
Codename: n/a"
}
fn fedora_file() -> &'static str {
"\nLSB Version: :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch\n\
Distributor ID: Fedora\n\
Description: Fedora release 26 (Twenty Six)\n\
Release: 26\n\
Codename: TwentySix\n\
"
}
fn ubuntu_file() -> &'static str {
"Distributor ID: Ubuntu\n\
Description: Ubuntu 16.04.5 LTS\n\
Release: 16.04\n\
Codename: xenial"
}
fn mint_file() -> &'static str {
"Distributor ID: Linuxmint\n\
Description: Linux Mint 20\n\
Release: 20\n\
Codename: ulyana"
}
fn nixos_file() -> &'static str {
"Distributor ID: NixOS\n\
Description: NixOS 21.05 (Okapi)\n\
Release: 21.05pre275822.916ee862e87\n\
Codename: okapi"
}
fn amazon1_file() -> &'static str {
"LSB Version: :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch\n\
Distributor ID: AmazonAMI\n\
Description: Amazon Linux AMI release 2018.03\n\
Release: 2018.03\n\
Codename: n/a\n\
"
}
fn amazon2_file() -> &'static str {
"LSB Version: :core-4.1-amd64:core-4.1-noarch\n\
Distributor ID: Amazon\n\
Description: Amazon Linux release 2 (Karoo)\n\
Release: 2\n\
Codename: Karoo\n\
"
}
fn rhel8_file() -> &'static str {
"LSB Version: :core-4.1-amd64:core-4.1-noarch\n\
Distributor ID: RedHatEnterprise\n\
Description: Red Hat Enterprise Linux release 8.1 (Ootpa)\n\
Release: 8.1\n\
Codename: Ootpa\n\
"
}
fn rhel7_file() -> &'static str {
"LSB Version: :core-4.1-amd64:core-4.1-noarch\n\
Distributor ID: RedHatEnterpriseServer\n\
Description: Red Hat Enterprise Linux Server release 7.7 (Maipo)\n\
Release: 7.7\n\
Codename: Maipo\n\
"
}
fn rhel6_file() -> &'static str {
"LSB Version: :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch\n\
Distributor ID: RedHatEnterpriseServer\n\
Description: Red Hat Enterprise Linux Server release 6.10 (Santiago)\n\
Release: 6.10\n\
Codename: Santiago\n\
"
}
fn suse_enterprise15_1_file() -> &'static str {
"LSB Version: n/a\n\
Distributor ID: SUSE\n\
Description: SUSE Linux Enterprise Server 15 SP1\n\
Release: 15.1\n\
Codename: n/a\n\
"
}
fn suse_enterprise12_5_file() -> &'static str {
"LSB Version: n/a\n\
Distributor ID: SUSE\n\
Description: SUSE Linux Enterprise Server 12 SP5\n\
Release: 12.5\n\
Codename: n/a\n\
"
}
fn raspberry_os_file() -> &'static str {
"LSB Version: n/a\n\
Distributor ID: Raspbian\n\
Description: Raspbian GNU/Linux 10 (buster)\n\
Release: 10\n\
Codename: n/a\n\
"
}
fn open_suse_15_1_file() -> &'static str {
"LSB Version: n/a\n\
Distributor ID: openSUSE\n\
Description: openSUSE Leap 15.1\n\
Release: 15.1\n\
Codename: n/a\n\
"
}
fn oracle_server_linux_7_5_file() -> &'static str {
"LSB Version: :core-4.1-amd64:core-4.1-noarch\n\
Distributor ID: OracleServer\n\
Description: Oracle Linux Server release 7.5\n\
Release: 7.5\n\
Codename: n/a\n\
"
}
fn oracle_server_linux_8_1_file() -> &'static str {
"LSB Version: :core-4.1-amd64:core-4.1-noarch\n\
Distributor ID: OracleServer\n\
Description: Oracle Linux Server release 8.1\n\
Release: 8.1\n\
Codename: n/a\n\
"
}
fn pop_os_20_04_lts_file() -> &'static str {
"No LSB modules are available.\n\
Distributor ID: Pop\n\
Description: Pop!_OS 20.04 LTS\n\
Release: 20.04\n\
Codename: focal\n\
"
}
fn solus_4_1_file() -> &'static str {
"LSB Version: 1.4\n\
Distributor ID: Solus\n\
Description: Solus\n\
Release: 4.1\n\
Codename: fortitude\n\
"
}
fn manjaro_19_0_2_file() -> &'static str {
"LSB Version: n/a\n\
Distributor ID: ManjaroLinux\n\
Description: Manjaro Linux\n\
Release: 19.0.2\n\
Codename: n/a\n\
"
}
fn mariner_file() -> &'static str {
"LSB Version: n/a\n\
Distributor ID: Mariner\n\
Description: CBL-Mariner 2.0.20220210\n\
Release: 2.0.20220210\n\
Codename: Mariner\n\
"
}
fn endeavouros_file() -> &'static str {
"LSB Version: 1.4\n\
Distributor ID: EndeavourOS\n\
Description: EndeavourOS Linux\n\
Release: rolling\n\
Codename: n/a\n\
"
}
}