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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! This module contains the implementation of the `pids` cgroup subsystem.
//!
//! See the Kernel's documentation for more information about this subsystem, found at:
//!  [Documentation/cgroups-v1/pids.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt)
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;

use error::*;
use error::ErrorKind::*;

use {
    ControllIdentifier, ControllerInternal, Controllers, PidResources, Resources, Subsystem,
};

/// A controller that allows controlling the `pids` subsystem of a Cgroup.
#[derive(Debug, Clone)]
pub struct PidController {
    base: PathBuf,
    path: PathBuf,
}

/// The values found in the `pids.max` file in a Cgroup's `pids` subsystem.
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum PidMax {
    /// This value is returned when the text found `pids.max` is `"max"`.
    Max,
    /// When the value in `pids.max` is a numerical value, they are returned via this enum field.
    Value(i64),
}

impl Default for PidMax {
    /// By default, (as per the kernel) `pids.max` should contain `"max"`.
    fn default() -> Self {
        PidMax::Max
    }
}

impl ControllerInternal for PidController {
    fn control_type(&self) -> Controllers {
        Controllers::Pids
    }
    fn get_path(&self) -> &PathBuf {
        &self.path
    }
    fn get_path_mut(&mut self) -> &mut PathBuf {
        &mut self.path
    }
    fn get_base(&self) -> &PathBuf {
        &self.base
    }

    fn apply(&self, res: &Resources) -> Result<()> {
        // get the resources that apply to this controller
        let pidres: &PidResources = &res.pid;

        if pidres.update_values {
            // apply pid_max
            let _ = self.set_pid_max(pidres.maximum_number_of_processes);

            // now, verify
            if self.get_pid_max()? == pidres.maximum_number_of_processes {
                return Ok(());
            } else {
                return Err(Error::new(Other));
            }
        }

        Ok(())
    }
}

// impl<'a> ControllIdentifier for &'a PidController {
//     fn controller_type() -> Controllers {
//         Controllers::Pids
//     }
// }

impl ControllIdentifier for PidController {
    fn controller_type() -> Controllers {
        Controllers::Pids
    }
}

impl<'a> From<&'a Subsystem> for &'a PidController {
    fn from(sub: &'a Subsystem) -> &'a PidController {
        unsafe {
            match sub {
                Subsystem::Pid(c) => c,
                _ => {
                    assert_eq!(1, 0);
                    ::std::mem::uninitialized()
                }
            }
        }
    }
}

fn read_u64_from(mut file: File) -> Result<u64> {
    let mut string = String::new();
    match file.read_to_string(&mut string) {
        Ok(_) => string.trim().parse().map_err(|e| Error::with_cause(ParseError, e)),
        Err(e) => Err(Error::with_cause(ReadFailed, e)),
    }
}

impl PidController {
    /// Constructors a new `PidController` instance, with `oroot` serving as the controller's root
    /// directory.
    pub fn new(oroot: PathBuf) -> Self {
        let mut root = oroot;
        root.push(Self::controller_type().to_string());
        Self {
            base: root.clone(),
            path: root,
        }
    }

    /// The number of times `fork` failed because the limit was hit.
    pub fn get_pid_events(&self) -> Result<u64> {
        self.open_path("pids.events", false).and_then(|mut file| {
            let mut string = String::new();
            match file.read_to_string(&mut string) {
                Ok(_) => match string.split_whitespace().nth(1) {
                    Some(elem) => match elem.parse() {
                        Ok(val) => Ok(val),
                        Err(e) => Err(Error::with_cause(ParseError, e)),
                    },
                    None => Err(Error::new(ParseError)),
                },
                Err(e) => Err(Error::with_cause(ReadFailed, e)),
            }
        })
    }

    /// The number of processes currently.
    pub fn get_pid_current(&self) -> Result<u64> {
        self.open_path("pids.current", false)
            .and_then(read_u64_from)
    }

    /// The maximum number of processes that can exist at one time in the control group.
    pub fn get_pid_max(&self) -> Result<PidMax> {
        self.open_path("pids.max", false).and_then(|mut file| {
            let mut string = String::new();
            let res = file.read_to_string(&mut string);
            match res {
                Ok(_) => if string.trim() == "max" {
                    Ok(PidMax::Max)
                } else {
                    match string.trim().parse() {
                        Ok(val) => Ok(PidMax::Value(val)),
                        Err(e) => Err(Error::with_cause(ParseError, e)),
                    }
                },
                Err(e) => Err(Error::with_cause(ReadFailed, e)),
            }
        })
    }

    /// Set the maximum number of processes that can exist in this control group.
    ///
    /// Note that if `get_pid_current()` returns a higher number than what you
    /// are about to set (`max_pid`), then no processess will be killed. Additonally, attaching
    /// extra processes to a control group disregards the limit.
    pub fn set_pid_max(&self, max_pid: PidMax) -> Result<()> {
        self.open_path("pids.max", true).and_then(|mut file| {
            let string_to_write = match max_pid {
                PidMax::Max => "max".to_string(),
                PidMax::Value(num) => num.to_string(),
            };
            match file.write_all(string_to_write.as_ref()) {
                Ok(_) => Ok(()),
                Err(e) => Err(Error::with_cause(WriteFailed, e)),
            }
        })
    }
}