sysctl/
ctl_value.rs

1// ctl_value.rs
2
3#[cfg(target_os = "freebsd")]
4use temperature::Temperature;
5use enum_as_inner::EnumAsInner;
6
7/// An Enum that holds all values returned by sysctl calls.
8/// Extract inner value with accessors like `as_int()`.
9///
10/// # Example
11///
12/// ```
13/// # use sysctl::Sysctl;
14/// if let Ok(ctl) = sysctl::Ctl::new("kern.osrevision") {
15///     if let Ok(r) = ctl.value() {
16///         if let Some(val) = r.as_int() {
17///             println!("Value: {}", val);
18///         }
19///     }
20/// }
21/// ```
22#[derive(Debug, EnumAsInner, PartialEq, PartialOrd)]
23pub enum CtlValue {
24    None,
25    Node(Vec<u8>),
26    Int(i32),
27    String(String),
28    S64(i64),
29    Struct(Vec<u8>),
30    Uint(u32),
31    Long(i64),
32    Ulong(u64),
33    U64(u64),
34    U8(u8),
35    U16(u16),
36    S8(i8),
37    S16(i16),
38    S32(i32),
39    U32(u32),
40    #[cfg(target_os = "freebsd")]
41    Temperature(Temperature),
42}
43
44impl std::fmt::Display for CtlValue {
45    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
46        let s = match self {
47            CtlValue::None => "[None]".to_owned(),
48            CtlValue::Int(i) => format!("{}", i),
49            CtlValue::Uint(i) => format!("{}", i),
50            CtlValue::Long(i) => format!("{}", i),
51            CtlValue::Ulong(i) => format!("{}", i),
52            CtlValue::U8(i) => format!("{}", i),
53            CtlValue::U16(i) => format!("{}", i),
54            CtlValue::U32(i) => format!("{}", i),
55            CtlValue::U64(i) => format!("{}", i),
56            CtlValue::S8(i) => format!("{}", i),
57            CtlValue::S16(i) => format!("{}", i),
58            CtlValue::S32(i) => format!("{}", i),
59            CtlValue::S64(i) => format!("{}", i),
60            CtlValue::Struct(_) => "[Opaque Struct]".to_owned(),
61            CtlValue::Node(_) => "[Node]".to_owned(),
62            CtlValue::String(s) => s.to_owned(),
63            #[cfg(target_os = "freebsd")]
64            CtlValue::Temperature(t) => format!("{}", t.kelvin()),
65        };
66        write!(f, "{}", s)
67    }
68}
69
70#[cfg(all(test, any(target_os = "linux", target_os = "android")))]
71mod tests_linux {
72    use crate::sys;
73    use crate::Sysctl;
74
75    #[test]
76    fn ctl_value_string() {
77        // NOTE: Some linux distributions require Root permissions
78        //        e.g Debian.
79        let output = std::process::Command::new("sysctl")
80            .arg("-n")
81            .arg("kernel.version")
82            .output()
83            .expect("failed to execute process");
84        let ver = String::from_utf8_lossy(&output.stdout);
85        let s = match sys::funcs::value("/proc/sys/kernel/version") {
86            Ok(crate::CtlValue::String(s)) => s,
87            _ => panic!("crate::value() returned Error"),
88        };
89        assert_eq!(s.trim(), ver.trim());
90
91        let kernversion = crate::Ctl::new("kernel.version").unwrap();
92        let s = match kernversion.value() {
93            Ok(crate::CtlValue::String(s)) => s,
94            _ => "...".into(),
95        };
96        assert_eq!(s.trim(), ver.trim());
97    }
98}
99
100#[cfg(all(test, any(target_os = "freebsd", target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos")))]
101mod tests_unix {
102    use crate::sys;
103    use crate::Sysctl;
104
105    #[test]
106    fn ctl_value_string() {
107        let output = std::process::Command::new("sysctl")
108            .arg("-n")
109            .arg("kern.version")
110            .output()
111            .expect("failed to execute process");
112        let ver = String::from_utf8_lossy(&output.stdout);
113        let ctl = crate::Ctl::new("kern.version").expect("Ctl::new");
114        let s = match ctl.value() {
115            Ok(crate::CtlValue::String(s)) => s,
116            _ => "...".into(),
117        };
118        assert_eq!(s.trim(), ver.trim());
119
120        let kernversion = crate::Ctl::new("kern.version").unwrap();
121        let s = match kernversion.value() {
122            Ok(crate::CtlValue::String(s)) => s,
123            _ => "...".into(),
124        };
125        assert_eq!(s.trim(), ver.trim());
126    }
127
128    #[test]
129    fn ctl_value_int() {
130        let output = std::process::Command::new("sysctl")
131            .arg("-n")
132            .arg("kern.osrevision")
133            .output()
134            .expect("failed to execute process");
135        let rev_str = String::from_utf8_lossy(&output.stdout);
136        let rev = rev_str.trim().parse::<i32>().unwrap();
137
138        let ctl =
139            crate::Ctl::new("kern.osrevision").expect("Could not get kern.osrevision sysctl.");
140        let n = match ctl.value() {
141            Ok(crate::CtlValue::Int(n)) => n,
142            Ok(_) => 0,
143            Err(_) => 0,
144        };
145        assert_eq!(n, rev);
146    }
147
148    #[test]
149    fn ctl_value_oid_int() {
150        let output = std::process::Command::new("sysctl")
151            .arg("-n")
152            .arg("kern.osrevision")
153            .output()
154            .expect("failed to execute process");
155        let rev_str = String::from_utf8_lossy(&output.stdout);
156        let rev = rev_str.trim().parse::<i32>().unwrap();
157        let n = match sys::funcs::value_oid(&mut vec![libc::CTL_KERN, libc::KERN_OSREV]) {
158            Ok(crate::CtlValue::Int(n)) => n,
159            Ok(_) => 0,
160            Err(_) => 0,
161        };
162        assert_eq!(n, rev);
163    }
164
165    #[test]
166    fn ctl_struct_type() {
167        let info = crate::CtlInfo {
168            ctl_type: crate::CtlType::Int,
169            fmt: "S,TYPE".into(),
170            flags: 0,
171        };
172
173        assert_eq!(info.struct_type(), Some("TYPE".into()));
174
175        let info = crate::CtlInfo {
176            ctl_type: crate::CtlType::Int,
177            fmt: "I".into(),
178            flags: 0,
179        };
180        assert_eq!(info.struct_type(), None);
181    }
182}