1use std::ffi::CStr;
4use std::fs;
5use std::mem::zeroed;
6use std::str::from_utf8;
7use crate::Version;
8
9#[cfg(not(target_os = "linux"))]
10pub fn version() -> Option<Version> {
11 Version::parse(&release()?)
12}
13
14#[cfg(target_os = "linux")]
15pub fn version() -> Option<Version> {
16 ubuntu(read("/proc/version_signature")).or_else(|| {
17 debian(read("/proc/version")).or_else(|| {
18 Version::parse(&release()?)
19 })
20 })
21}
22
23pub(crate) fn release() -> Option<String> {
24 Some(unsafe {
25 let mut buf = zeroed();
26 match libc::uname(&mut buf) {
27 0 => CStr::from_ptr(buf.release[..].as_ptr()),
28 _ => return None,
29 }.to_string_lossy().to_string()
30 })
31}
32
33#[allow(dead_code)]
34pub(crate) fn debian(line: Option<String>) -> Option<Version> {
35 let line = line?;
36 let marker = "Debian ";
37 let index = line.rfind(marker)? + marker.len();
38 Version::parse(&line[index..])
39}
40
41#[allow(dead_code)]
42pub(crate) fn ubuntu(line: Option<String>) -> Option<Version> {
43 let (name, version) = triple(&line?)?;
44 match name.as_str() {
45 "Ubuntu" => Some(version),
46 _ => None,
47 }
48}
49
50fn triple(line: &str) -> Option<(String, Version)> {
51 let mut split = line.splitn(3, char::is_whitespace);
52 let name = split.next()?;
53 let _ = split.next()?;
54 let upstream = split.next()?;
55 let version = Version::parse(&upstream)?;
56 Some((name.to_string(), version))
57}
58
59#[allow(dead_code)]
60fn read(path: &str) -> Option<String> {
61 match from_utf8(&fs::read(path).ok()?) {
62 Ok(line) => Some(line.to_string()),
63 Err(_) => None,
64 }
65}