sysctl/linux/
ctl_iter.rs

1// linux/ctl_iter.rs
2
3use super::ctl::Ctl;
4use ctl_error::SysctlError;
5use traits::Sysctl;
6
7/// An iterator over Sysctl entries.
8pub struct CtlIter {
9    direntries: Vec<walkdir::DirEntry>,
10    base: String,
11    cur_idx: usize,
12}
13
14impl CtlIter {
15    /// Return an iterator over the complete sysctl tree.
16    pub fn root() -> Self {
17        let entries: Vec<walkdir::DirEntry> = walkdir::WalkDir::new("/proc/sys")
18            .sort_by(|a, b| a.path().cmp(b.path()))
19            .follow_links(false)
20            .into_iter()
21            .filter_map(|e| e.ok())
22            .filter(|e| e.file_type().is_file())
23            .collect();
24        CtlIter {
25            direntries: entries,
26            base: "/proc/sys".to_owned(),
27            cur_idx: 0,
28        }
29    }
30
31    /// Return an iterator over all sysctl entries below the given node.
32    pub fn below(node: Ctl) -> Self {
33        let root = node.path();
34        let entries: Vec<walkdir::DirEntry> = walkdir::WalkDir::new(&root)
35            .sort_by(|a, b| a.path().cmp(b.path()))
36            .follow_links(false)
37            .into_iter()
38            .filter_map(|e| e.ok())
39            .filter(|e| e.file_type().is_file())
40            .collect();
41        CtlIter {
42            direntries: entries,
43            base: root,
44            cur_idx: 0,
45        }
46    }
47}
48
49impl Iterator for CtlIter {
50    type Item = Result<Ctl, SysctlError>;
51
52    fn next(&mut self) -> Option<Self::Item> {
53        if self.cur_idx >= self.direntries.len() {
54            return None;
55        }
56
57        let e: &walkdir::DirEntry = &self.direntries[self.cur_idx];
58        self.cur_idx += 1;
59
60        // We continue iterating as long as the oid starts with the base
61        if let Some(path) = e.path().to_str() {
62            if path.starts_with(&self.base) {
63                Some(Ctl::new(path))
64            } else {
65                None
66            }
67        } else {
68            Some(Err(SysctlError::ParseError))
69        }
70    }
71}
72
73/// Ctl implements the IntoIterator trait to allow for easy iteration
74/// over nodes.
75///
76/// # Example
77///
78/// ```
79/// # use sysctl::Sysctl;
80/// #
81/// let kern = sysctl::Ctl::new("kernel");
82/// for ctl in kern {
83///     println!("{}", ctl.name().unwrap());
84/// }
85/// ```
86impl IntoIterator for Ctl {
87    type Item = Result<Ctl, SysctlError>;
88    type IntoIter = CtlIter;
89
90    fn into_iter(self: Self) -> Self::IntoIter {
91        CtlIter::below(self)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use crate::Sysctl;
98
99    #[test]
100    fn ctl_iter_iterate_all() {
101        let root = crate::CtlIter::root();
102        let all_ctls: Vec<super::Ctl> = root.into_iter().filter_map(Result::ok).collect();
103        assert_ne!(all_ctls.len(), 0);
104        for ctl in &all_ctls {
105            println!("{:?}", ctl.name());
106        }
107    }
108
109    #[test]
110    fn ctl_iter_below_compare_outputs() {
111        // NOTE: Some linux distributions require Root permissions
112        //        e.g Debian.
113        let output = std::process::Command::new("sysctl")
114            .arg("user")
115            .output()
116            .expect("failed to execute process");
117        let expected = String::from_utf8_lossy(&output.stdout);
118
119        let node = crate::Ctl::new("user").expect("could not get node");
120        let ctls = crate::CtlIter::below(node);
121        let mut actual: Vec<String> = vec![];
122
123        for ctl in ctls {
124            let ctl = match ctl {
125                Err(_) => panic!("ctl error"),
126                Ok(s) => s,
127            };
128
129            let name = match ctl.name() {
130                Ok(s) => s,
131                Err(_) => panic!("get ctl name"),
132            };
133
134            match ctl.value_type().expect("could not get value type") {
135                crate::CtlType::String => {
136                    actual.push(format!(
137                        "{} = {}",
138                        name,
139                        ctl.value_string()
140                            .expect(&format!("could not get value as string for {}", name))
141                    ));
142                }
143                _ => panic!("sysctl not string type"),
144            };
145        }
146        assert_eq!(actual.join("\n").trim(), expected.trim());
147    }
148}