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
//! This module contains the implementation of the `net_prio` cgroup subsystem.
//!
//! See the Kernel's documentation for more information about this subsystem, found at:
//!  [Documentation/cgroup-v1/net_prio.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/net_prio.txt)
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf;

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

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

/// A controller that allows controlling the `net_prio` subsystem of a Cgroup.
///
/// In essence, using `net_prio` one can set the priority of the packets emitted from the control
/// group's tasks. This can then be used to have QoS restrictions on certain control groups and
/// thus, prioritizing certain tasks.
#[derive(Debug, Clone)]
pub struct NetPrioController {
    base: PathBuf,
    path: PathBuf,
}

impl ControllerInternal for NetPrioController {
    fn control_type(&self) -> Controllers {
        Controllers::NetPrio
    }
    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 res: &NetworkResources = &res.network;

        if res.update_values {
            for i in &res.priorities {
                let _ = self.set_if_prio(&i.name, i.priority);
            }
        }

        Ok(())
    }
}

impl ControllIdentifier for NetPrioController {
    fn controller_type() -> Controllers {
        Controllers::NetPrio
    }
}

impl<'a> From<&'a Subsystem> for &'a NetPrioController {
    fn from(sub: &'a Subsystem) -> &'a NetPrioController {
        unsafe {
            match sub {
                Subsystem::NetPrio(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 NetPrioController {
    /// Constructs a new `NetPrioController` with `oroot` serving as the root of the control group.
    pub fn new(oroot: PathBuf) -> Self {
        let mut root = oroot;
        root.push(Self::controller_type().to_string());
        Self {
            base: root.clone(),
            path: root,
        }
    }

    /// Retrieves the current priority of the emitted packets.
    pub fn prio_idx(&self) -> u64 {
        self.open_path("net_prio.prioidx", false)
            .and_then(read_u64_from)
            .unwrap_or(0)
    }

    /// A map of priorities for each network interface.
    pub fn ifpriomap(&self) -> Result<HashMap<String, u64>> {
        self.open_path("net_prio.ifpriomap", false)
            .and_then(|file| {
                let bf = BufReader::new(file);
                bf.lines().fold(Ok(HashMap::new()), |acc, line| {
                    if acc.is_err() {
                        acc
                    } else {
                        let mut acc = acc.unwrap();
                        let l = line.unwrap();
                        let mut sp = l.split_whitespace();
                        let ifname = sp.nth(0);
                        let ifprio = sp.nth(1);
                        if ifname.is_none() || ifprio.is_none() {
                            Err(Error::new(ParseError))
                        } else {
                            let ifname = ifname.unwrap();
                            let ifprio = ifprio.unwrap().trim().parse();
                            match ifprio {
                                Err(e) => Err(Error::with_cause(ParseError, e)),
                                Ok(_) => {
                                    acc.insert(ifname.to_string(), ifprio.unwrap());
                                    Ok(acc)
                                }
                            }
                        }
                    }
                })
            })
    }

    /// Set the priority of the network traffic on `eif` to be `prio`.
    pub fn set_if_prio(&self, eif: &str, prio: u64) -> Result<()> {
        self.open_path("net_prio.ifpriomap", true)
            .and_then(|mut file| {
                file.write_all(format!("{} {}", eif, prio).as_ref())
                    .map_err(|e| Error::with_cause(WriteFailed, e))
            })
    }
}