Skip to main content

onde_sysctl/unix/
ctl.rs

1// unix/ctl.rs
2
3use super::funcs::*;
4use crate::ctl_error::SysctlError;
5use crate::ctl_flags::CtlFlags;
6use crate::ctl_info::CtlInfo;
7use crate::ctl_type::CtlType;
8use crate::ctl_value::CtlValue;
9use crate::traits::Sysctl;
10use std::str::FromStr;
11
12/// This struct represents a system control.
13#[derive(Debug, Clone, PartialEq)]
14pub enum Ctl {
15    Oid(Vec<libc::c_int>),
16    Name(String, CtlType, String),
17}
18
19impl Ctl {
20    pub fn oid(&self) -> Option<&Vec<libc::c_int>> {
21        match self {
22            Ctl::Oid(oid) => Some(oid),
23            _ => None,
24        }
25    }
26}
27
28impl std::str::FromStr for Ctl {
29    type Err = SysctlError;
30
31    #[allow(clippy::redundant_field_names)]
32    fn from_str(s: &str) -> Result<Self, Self::Err> {
33        let oid = name2oid(s)?;
34
35        Ok(Ctl::Oid(oid))
36    }
37}
38
39impl Sysctl for Ctl {
40    fn new(name: &str) -> Result<Self, SysctlError> {
41        Ctl::from_str(name)
42    }
43
44    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
45    fn new_with_type(name: &str, _ctl_type: CtlType, _fmt: &str) -> Result<Self, SysctlError> {
46        Ctl::from_str(name)
47    }
48
49    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
50    fn new_with_type(name: &str, ctl_type: CtlType, fmt: &str) -> Result<Self, SysctlError> {
51        let _ = name2oid(name)?;
52
53        Ok(Ctl::Name(name.to_string(), ctl_type, fmt.to_string()))
54    }
55
56    fn name(&self) -> Result<String, SysctlError> {
57        match self {
58            Ctl::Oid(oid) => oid2name(&oid),
59            Ctl::Name(name, ..) => Ok(name.clone()),
60        }
61    }
62
63    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
64    fn value_type(&self) -> Result<CtlType, SysctlError> {
65        match self {
66            Ctl::Oid(oid) => {
67                let info = oidfmt(oid)?;
68                Ok(info.ctl_type)
69            }
70            Ctl::Name(_, ctl_type, _) => {
71                Ok(*ctl_type)
72            }
73        }
74    }
75
76    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
77    fn value_type(&self) -> Result<CtlType, SysctlError> {
78        match self {
79            Ctl::Oid(oid) => {
80                let info = oidfmt(oid)?;
81
82                Ok(match info.ctl_type {
83                    CtlType::Int => match info.fmt.as_str() {
84                        "I" => CtlType::Int,
85                        "IU" => CtlType::Uint,
86                        "L" => CtlType::Long,
87                        "LU" => CtlType::Ulong,
88                        _ => return Err(SysctlError::MissingImplementation),
89                    },
90                    ctl_type => ctl_type,
91                })
92            }
93            Ctl::Name(_, ctl_type, fmt) => {
94                Ok(match ctl_type {
95                    CtlType::Int => match fmt.as_str() {
96                        "I" => CtlType::Int,
97                        "IU" => CtlType::Uint,
98                        "L" => CtlType::Long,
99                        "LU" => CtlType::Ulong,
100                        _ => return Err(SysctlError::MissingImplementation),
101                    },
102                    ctl_type => *ctl_type,
103                })
104            }
105        }
106    }
107
108    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
109    fn description(&self) -> Result<String, SysctlError> {
110        let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
111        oid2description(oid)
112    }
113
114    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
115    fn description(&self) -> Result<String, SysctlError> {
116        Ok("[N/A]".to_string())
117    }
118
119    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
120    fn value(&self) -> Result<CtlValue, SysctlError> {
121        let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
122        value_oid(oid)
123    }
124
125    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
126    fn value(&self) -> Result<CtlValue, SysctlError> {
127        match self {
128            Ctl::Oid(oid) => {
129                let mut oid = oid.clone();
130                value_oid(&mut oid)
131            }
132            Ctl::Name(name, ctl_type, fmt) => {
133                value_name(name.as_str(), *ctl_type, fmt.as_str())
134            }
135        }
136    }
137
138    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
139    fn value_as<T>(&self) -> Result<Box<T>, SysctlError> {
140        let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
141        value_oid_as::<T>(oid)
142    }
143
144    fn value_string(&self) -> Result<String, SysctlError> {
145        self.value().map(|v| format!("{}", v))
146    }
147
148    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
149    fn value_as<T>(&self) -> Result<Box<T>, SysctlError> {
150        match self {
151            Ctl::Oid(oid) => {
152                let mut oid = oid.clone();
153                value_oid_as::<T>(&mut oid)
154            }
155            Ctl::Name(name, ctl_type, fmt) => {
156                value_name_as::<T>(name.as_str(), *ctl_type, fmt.as_str())
157            }
158        }
159    }
160
161    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
162    fn set_value(&self, value: CtlValue) -> Result<CtlValue, SysctlError> {
163        let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
164        set_oid_value(&oid, value)
165    }
166
167    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
168    fn set_value(&self, value: CtlValue) -> Result<CtlValue, SysctlError> {
169        match self {
170            Ctl::Oid(oid) => {
171                let mut oid = oid.clone();
172                set_oid_value(&mut oid, value)
173            }
174            Ctl::Name(name, ctl_type, fmt) => {
175                set_name_value(name.as_str(), *ctl_type, fmt.as_str(), value)
176            }
177        }
178    }
179
180    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
181    fn set_value_string(&self, value: &str) -> Result<String, SysctlError> {
182        let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
183        let ctl_type = self.value_type()?;
184        let _ = match ctl_type {
185            CtlType::String => set_oid_value(oid, CtlValue::String(value.to_owned())),
186            CtlType::Uint => set_oid_value(
187                oid,
188                CtlValue::Uint(value.parse::<u32>().map_err(|_| SysctlError::ParseError)?),
189            ),
190            CtlType::Int => set_oid_value(
191                oid,
192                CtlValue::Int(value.parse::<i32>().map_err(|_| SysctlError::ParseError)?),
193            ),
194            CtlType::Ulong => set_oid_value(
195                oid,
196                CtlValue::Ulong(value.parse::<u64>().map_err(|_| SysctlError::ParseError)?),
197            ),
198            CtlType::U8 => set_oid_value(
199                oid,
200                CtlValue::U8(value.parse::<u8>().map_err(|_| SysctlError::ParseError)?),
201            ),
202            _ => Err(SysctlError::MissingImplementation),
203        }?;
204        self.value_string()
205    }
206
207    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
208    fn set_value_string(&self, value: &str) -> Result<String, SysctlError> {
209        let ctl_type = self.value_type()?;
210
211        match self {
212            Ctl::Oid(oid) => {
213                let mut oid = oid.clone();
214                let _ = match ctl_type {
215                    CtlType::String => set_oid_value(&mut oid, CtlValue::String(value.to_owned())),
216                    CtlType::Uint => set_oid_value(
217                        &mut oid,
218                        CtlValue::Uint(value.parse::<u32>().map_err(|_| SysctlError::ParseError)?),
219                    ),
220                    CtlType::Int => set_oid_value(
221                        &mut oid,
222                        CtlValue::Int(value.parse::<i32>().map_err(|_| SysctlError::ParseError)?),
223                    ),
224                    CtlType::Ulong => set_oid_value(
225                        &mut oid,
226                        CtlValue::Ulong(value.parse::<u64>().map_err(|_| SysctlError::ParseError)?),
227                    ),
228                    CtlType::U8 => set_oid_value(
229                        &mut oid,
230                        CtlValue::U8(value.parse::<u8>().map_err(|_| SysctlError::ParseError)?),
231                    ),
232                    _ => Err(SysctlError::MissingImplementation),
233                }?;
234            }
235            Ctl::Name(name, ctl_type, fmt) => {
236                let _ = match ctl_type {
237                    CtlType::String => set_name_value(
238                        name.as_str(),
239                        *ctl_type,
240                        fmt.as_str(),
241                        CtlValue::String(value.to_owned()),
242                    ),
243                    CtlType::Uint => set_name_value(
244                        name.as_str(),
245                        *ctl_type,
246                        fmt.as_str(),
247                        CtlValue::Uint(value.parse::<u32>().map_err(|_| SysctlError::ParseError)?),
248                    ),
249                    CtlType::Int => set_name_value(
250                        name.as_str(),
251                        *ctl_type,
252                        fmt.as_str(),
253                        CtlValue::Int(value.parse::<i32>().map_err(|_| SysctlError::ParseError)?),
254                    ),
255                    CtlType::Ulong => set_name_value(
256                        name.as_str(),
257                        *ctl_type,
258                        fmt.as_str(),
259                        CtlValue::Ulong(value.parse::<u64>().map_err(|_| SysctlError::ParseError)?),
260                    ),
261                    CtlType::U8 => set_name_value(
262                        name.as_str(),
263                        *ctl_type,
264                        fmt.as_str(),
265                        CtlValue::U8(value.parse::<u8>().map_err(|_| SysctlError::ParseError)?),
266                    ),
267                    _ => Err(SysctlError::MissingImplementation),
268                }?;
269            }
270        }
271
272        self.value_string()
273    }
274
275    fn flags(&self) -> Result<CtlFlags, SysctlError> {
276        Ok(self.info()?.flags())
277    }
278
279    fn info(&self) -> Result<CtlInfo, SysctlError> {
280        let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
281        oidfmt(oid)
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use crate::Sysctl;
288
289    #[test]
290    fn ctl_new() {
291        let _ = super::Ctl::new("kern.ostype").expect("Ctl::new");
292    }
293
294    #[test]
295    fn ctl_description() {
296        let ctl = super::Ctl::new("kern.ostype").expect("Ctl::new");
297
298        let descp = ctl.description();
299        assert!(descp.is_ok());
300
301        let descp = descp.unwrap();
302
303        #[cfg(target_os = "freebsd")]
304        assert_eq!(descp, "Operating system type");
305
306        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos", target_os = "linux"))]
307        assert_eq!(descp, "[N/A]");
308    }
309}
310
311#[cfg(all(test, target_os = "freebsd"))]
312mod tests_freebsd {}