-
Notifications
You must be signed in to change notification settings - Fork 315
/
linux.rs
115 lines (102 loc) · 3.4 KB
/
linux.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::{run_ping, Parser, PingDetectionError, PingResult, Pinger};
use anyhow::Context;
use lazy_regex::*;
use std::time::Duration;
#[derive(Debug, Eq, PartialEq)]
pub enum LinuxPingType {
BusyBox,
IPTools,
}
pub fn detect_linux_ping() -> Result<LinuxPingType, PingDetectionError> {
let child = run_ping("ping", vec!["-V".to_string()])?;
let output = child
.wait_with_output()
.context("Error getting ping stdout/stderr")?;
let stdout = String::from_utf8(output.stdout).context("Error decoding ping stdout")?;
let stderr = String::from_utf8(output.stderr).context("Error decoding ping stderr")?;
if stderr.contains("BusyBox") {
Ok(LinuxPingType::BusyBox)
} else if stdout.contains("iputils") {
Ok(LinuxPingType::IPTools)
} else if stdout.contains("inetutils") {
Err(PingDetectionError::NotSupported {
alternative: "Please use iputils ping, not inetutils.".to_string(),
})
} else {
let first_two_lines_stderr: Vec<String> =
stderr.lines().take(2).map(str::to_string).collect();
let first_two_lines_stout: Vec<String> =
stdout.lines().take(2).map(str::to_string).collect();
Err(PingDetectionError::UnknownPing {
stdout: first_two_lines_stout,
stderr: first_two_lines_stderr,
})
}
}
pub struct LinuxPinger {
interval: Duration,
interface: Option<String>,
}
impl Pinger for LinuxPinger {
type Parser = LinuxParser;
fn new(interval: Duration, interface: Option<String>) -> Self {
Self {
interval,
interface,
}
}
fn ping_args(&self, target: String) -> (&str, Vec<String>) {
// The -O flag ensures we "no answer yet" messages from ping
// See https://superuser.com/questions/270083/linux-ping-show-time-out
let mut args = vec![
"-O".to_string(),
format!("-i{:.1}", self.interval.as_millis() as f32 / 1_000_f32),
];
if let Some(interface) = &self.interface {
args.push("-I".into());
args.push(interface.clone());
}
args.push(target);
("ping", args)
}
}
pub struct AlpinePinger {}
// Alpine doesn't support timeout notifications, so we don't add the -O flag here
impl Pinger for AlpinePinger {
type Parser = LinuxParser;
fn new(_interval: Duration, _interface: Option<String>) -> Self {
Self {}
}
}
pub static UBUNTU_RE: Lazy<Regex> = lazy_regex!(r"(?i-u)time=(?P<ms>\d+)(?:\.(?P<ns>\d+))? *ms");
#[derive(Default)]
pub struct LinuxParser {}
impl Parser for LinuxParser {
fn parse(&self, line: String) -> Option<PingResult> {
if line.starts_with("64 bytes from") {
return self.extract_regex(&UBUNTU_RE, line);
} else if line.starts_with("no answer yet") {
return Some(PingResult::Timeout(line));
}
None
}
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(target_os = "linux")]
fn test_linux_detection() {
use super::*;
use os_info::Type;
let ping_type = detect_linux_ping().expect("Error getting ping");
match os_info::get().os_type() {
Type::Alpine => {
assert_eq!(ping_type, LinuxPingType::BusyBox)
}
Type::Ubuntu => {
assert_eq!(ping_type, LinuxPingType::IPTools)
}
_ => {}
}
}
}