Skip to content

Commit 0ea48ce

Browse files
committed
implement CtlIter iterator
1 parent 32fcb50 commit 0ea48ce

File tree

2 files changed

+288
-1
lines changed

2 files changed

+288
-1
lines changed

examples/iterate.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
extern crate sysctl;
2+
use std::env;
3+
4+
fn format_value(value: sysctl::CtlValue) -> String {
5+
match value {
6+
sysctl::CtlValue::None => "(none)".to_owned(),
7+
sysctl::CtlValue::Int(i) => format!("{}", i),
8+
sysctl::CtlValue::Uint(i) => format!("{}", i),
9+
sysctl::CtlValue::Long(i) => format!("{}", i),
10+
sysctl::CtlValue::Ulong(i) => format!("{}", i),
11+
sysctl::CtlValue::U8(i) => format!("{}", i),
12+
sysctl::CtlValue::U16(i) => format!("{}", i),
13+
sysctl::CtlValue::U32(i) => format!("{}", i),
14+
sysctl::CtlValue::U64(i) => format!("{}", i),
15+
sysctl::CtlValue::S8(i) => format!("{}", i),
16+
sysctl::CtlValue::S16(i) => format!("{}", i),
17+
sysctl::CtlValue::S32(i) => format!("{}", i),
18+
sysctl::CtlValue::S64(i) => format!("{}", i),
19+
sysctl::CtlValue::Struct(_) => "(opaque struct)".to_owned(),
20+
sysctl::CtlValue::Node(_) => "(node)".to_owned(),
21+
sysctl::CtlValue::String(s) => s.to_owned(),
22+
#[cfg(not(target_os = "macos"))]
23+
sysctl::CtlValue::Temperature(t) => format!("{} °C", t.celsius()),
24+
}
25+
}
26+
27+
fn print_ctl(ctl: &sysctl::Ctl) {
28+
let name = ctl.name().expect("Could not get name of control");
29+
30+
if let Ok(value) = ctl.value() {
31+
println!("{}: {}", name, format_value(value));
32+
}
33+
}
34+
35+
fn main() {
36+
let args: Vec<_> = env::args().collect();
37+
38+
let ctls = match args.len() {
39+
1 => sysctl::CtlIter::root().filter_map(Result::ok),
40+
2 => {
41+
let root = sysctl::Ctl::new(&args[1]).expect("Could not get given root node.");
42+
43+
let value_type = root.value_type()
44+
.expect("could not get value type of given sysctl");
45+
if value_type != sysctl::CtlType::Node {
46+
print_ctl(&root);
47+
return;
48+
}
49+
50+
root.into_iter().filter_map(Result::ok)
51+
}
52+
_ => panic!("more than 1 command-line argument given"),
53+
};
54+
55+
for ctl in ctls {
56+
print_ctl(&ctl);
57+
}
58+
}

src/lib.rs

Lines changed: 230 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1164,8 +1164,87 @@ fn oid2name(oid: &Vec<c_int>) -> Result<String, SysctlError> {
11641164
}
11651165
}
11661166

1167+
/// Get the next OID.
1168+
#[cfg(not(target_os = "macos"))]
1169+
pub fn next_oid(oid: &Vec<c_int>) -> Result<Option<Vec<c_int>>, SysctlError> {
1170+
// Request command for next oid
1171+
let mut qoid: Vec<c_int> = vec![0, 2];
1172+
qoid.extend(oid);
1173+
1174+
let mut len: usize = CTL_MAXNAME as usize * mem::size_of::<c_int>();
1175+
1176+
// We get results in this vector
1177+
let mut res: Vec<c_int> = vec![0; CTL_MAXNAME as usize];
1178+
1179+
let ret = unsafe {
1180+
sysctl(
1181+
qoid.as_ptr(),
1182+
qoid.len() as u32,
1183+
res.as_mut_ptr() as *mut c_void,
1184+
&mut len,
1185+
ptr::null(),
1186+
0,
1187+
)
1188+
};
1189+
if ret != 0 {
1190+
let e = io::Error::last_os_error();
1191+
1192+
if e.raw_os_error() == Some(libc::ENOENT) {
1193+
return Ok(None);
1194+
}
1195+
return Err(SysctlError::IoError(e));
1196+
}
1197+
1198+
// len is in bytes, convert to number of c_ints
1199+
len /= mem::size_of::<c_int>();
1200+
1201+
// Trim result vector
1202+
res.truncate(len);
1203+
1204+
Ok(Some(res))
1205+
}
1206+
1207+
#[cfg(target_os = "macos")]
1208+
pub fn next_oid(oid: &Vec<c_int>) -> Result<Option<Vec<c_int>>, SysctlError> {
1209+
// Request command for next oid
1210+
let mut qoid: Vec<c_int> = vec![0, 2];
1211+
qoid.extend(oid);
1212+
1213+
let mut len: usize = CTL_MAXNAME as usize * mem::size_of::<c_int>();
1214+
1215+
// We get results in this vector
1216+
let mut res: Vec<c_int> = vec![0; CTL_MAXNAME as usize];
1217+
1218+
let ret = unsafe {
1219+
sysctl(
1220+
qoid.as_mut_ptr(),
1221+
qoid.len() as u32,
1222+
res.as_mut_ptr() as *mut c_void,
1223+
&mut len,
1224+
ptr::null_mut(),
1225+
0,
1226+
)
1227+
};
1228+
if ret != 0 {
1229+
let e = io::Error::last_os_error();
1230+
1231+
if e.raw_os_error() == Some(libc::ENOENT) {
1232+
return Ok(None);
1233+
}
1234+
return Err(SysctlError::IoError(e));
1235+
}
1236+
1237+
// len is in bytes, convert to number of c_ints
1238+
len /= mem::size_of::<c_int>();
1239+
1240+
// Trim result vector
1241+
res.truncate(len);
1242+
1243+
Ok(Some(res))
1244+
}
1245+
11671246
/// This struct represents a system control.
1168-
#[derive(Debug, PartialEq)]
1247+
#[derive(Debug, Clone, PartialEq)]
11691248
pub struct Ctl {
11701249
oid: Vec<c_int>,
11711250
}
@@ -1345,6 +1424,79 @@ impl Ctl {
13451424
}
13461425
}
13471426

1427+
/// An iterator over Sysctl entries.
1428+
pub struct CtlIter {
1429+
// if we are iterating over a Node, only include OIDs
1430+
// starting with this base. Set to None if iterating over all
1431+
// OIDs.
1432+
base: Ctl,
1433+
current: Ctl,
1434+
}
1435+
1436+
impl CtlIter {
1437+
/// Return an iterator over the complete sysctl tree.
1438+
pub fn root() -> Self {
1439+
CtlIter{
1440+
base: Ctl { oid: vec![] },
1441+
current: Ctl { oid: vec![1] },
1442+
}
1443+
}
1444+
1445+
/// Return an iterator over all sysctl entries below the given node.
1446+
pub fn below(node: Ctl) -> Self {
1447+
CtlIter{
1448+
base: node.clone(),
1449+
current: node,
1450+
}
1451+
}
1452+
}
1453+
1454+
impl Iterator for CtlIter {
1455+
type Item = Result<Ctl,SysctlError>;
1456+
1457+
fn next(&mut self) -> Option<Self::Item> {
1458+
let oid = match next_oid(&self.current.oid) {
1459+
Ok(Some(o)) => o,
1460+
Err(e) => return Some(Err(e)),
1461+
Ok(None) => return None,
1462+
};
1463+
1464+
// We continue iterating as long as the oid starts with the base
1465+
let cont = oid.starts_with(&self.base.oid);
1466+
1467+
self.current = Ctl { oid };
1468+
1469+
match cont {
1470+
true => Some(Ok(self.current.clone())),
1471+
false => None,
1472+
}
1473+
}
1474+
}
1475+
1476+
/// Ctl implements the IntoIterator trait to allow for easy iteration
1477+
/// over nodes.
1478+
///
1479+
/// # Example
1480+
///
1481+
/// ```
1482+
/// extern crate sysctl;
1483+
/// use sysctl::Ctl;
1484+
///
1485+
/// let kern = Ctl::new("kern");
1486+
/// for ctl in kern {
1487+
/// let name = ctl.name().expect("could not get name");
1488+
/// println!("{}", name);
1489+
/// }
1490+
/// ```
1491+
impl IntoIterator for Ctl {
1492+
type Item = Result<Ctl,SysctlError>;
1493+
type IntoIter = CtlIter;
1494+
1495+
fn into_iter(self: Self) -> Self::IntoIter {
1496+
CtlIter::below(self)
1497+
}
1498+
}
1499+
13481500
#[cfg(test)]
13491501
mod tests {
13501502

@@ -1542,4 +1694,81 @@ mod tests {
15421694
assert!(false);
15431695
}
15441696
}
1697+
1698+
#[test]
1699+
fn ctl_iterate_all() {
1700+
let root = CtlIter::root();
1701+
1702+
let all_ctls = root.into_iter()
1703+
.filter_map(Result::ok);
1704+
1705+
for ctl in all_ctls {
1706+
println!("{:?}", ctl.name());
1707+
}
1708+
}
1709+
1710+
#[test]
1711+
fn ctl_iterate() {
1712+
let output = Command::new("sysctl")
1713+
.arg("security")
1714+
.output()
1715+
.expect("failed to execute process");
1716+
let expected = String::from_utf8_lossy(&output.stdout);
1717+
1718+
let security = Ctl::new("security")
1719+
.expect("could not get security node");
1720+
1721+
let ctls = CtlIter::below(security);
1722+
let mut actual : Vec<String> = vec!["".to_string()];
1723+
1724+
for ctl in ctls {
1725+
let ctl = match ctl {
1726+
Err(_) => { continue; },
1727+
Ok(s) => s,
1728+
};
1729+
1730+
let name = match ctl.name() {
1731+
Ok(s) => s,
1732+
Err(_) => { continue; },
1733+
};
1734+
1735+
let value = match ctl.value() {
1736+
Ok(s) => s,
1737+
Err(_) => { continue; },
1738+
};
1739+
1740+
let formatted = match value {
1741+
CtlValue::None => "(none)".to_owned(),
1742+
CtlValue::Int(i) => format!("{}", i),
1743+
CtlValue::Uint(i) => format!("{}", i),
1744+
CtlValue::Long(i) => format!("{}", i),
1745+
CtlValue::Ulong(i) => format!("{}", i),
1746+
CtlValue::U8(i) => format!("{}", i),
1747+
CtlValue::U16(i) => format!("{}", i),
1748+
CtlValue::U32(i) => format!("{}", i),
1749+
CtlValue::U64(i) => format!("{}", i),
1750+
CtlValue::S8(i) => format!("{}", i),
1751+
CtlValue::S16(i) => format!("{}", i),
1752+
CtlValue::S32(i) => format!("{}", i),
1753+
CtlValue::S64(i) => format!("{}", i),
1754+
CtlValue::Struct(_) => "(opaque struct)".to_owned(),
1755+
CtlValue::Node(_) => "(node)".to_owned(),
1756+
CtlValue::String(s) => s.to_owned(),
1757+
#[cfg(not(target_os = "macos"))]
1758+
CtlValue::Temperature(t) => format!("{} °C", t.celsius()),
1759+
};
1760+
1761+
match ctl.value_type().expect("could not get value type") {
1762+
CtlType::None => { continue; },
1763+
CtlType::Struct => { continue; },
1764+
CtlType::Node => { continue; },
1765+
#[cfg(not(target_os = "macos"))]
1766+
CtlType::Temperature => { continue; },
1767+
_ => {},
1768+
};
1769+
1770+
actual.push(format!("{}: {}", name, formatted));
1771+
}
1772+
assert_eq!(actual.join("\n").trim(), expected.trim());
1773+
}
15451774
}

0 commit comments

Comments
 (0)