1use std::collections::HashMap;
2use std::io::{BufRead, BufReader};
3use std::path::PathBuf;
4
5use pathrs::flags::OpenFlags;
6use pathrs::procfs::{ProcfsBase, ProcfsHandle};
7use procfs::ProcError;
8use procfs::process::MountInfo;
9
10use super::ControllerType;
11use super::controller_type::CONTROLLERS;
12
13#[derive(thiserror::Error, Debug)]
14pub enum V1MountPointError {
15 #[error("io error: {0}")]
16 Io(#[from] std::io::Error),
17 #[error("failed to get mountinfo: {0}")]
18 MountInfo(ProcError),
19 #[error("could not find mountpoint for {subsystem}")]
20 NotFound { subsystem: ControllerType },
21 #[error(transparent)]
22 Pathrs(#[from] pathrs::error::Error),
23}
24
25pub fn list_subsystem_mount_points() -> Result<Vec<PathBuf>, V1MountPointError> {
28 let reader = BufReader::new(ProcfsHandle::new()?.open(
29 ProcfsBase::ProcSelf,
30 "mountinfo",
31 OpenFlags::O_RDONLY | OpenFlags::O_CLOEXEC,
32 )?);
33
34 reader
35 .lines()
36 .map(|lr| {
37 lr.map_err(V1MountPointError::Io)
38 .and_then(|line| MountInfo::from_line(&line).map_err(V1MountPointError::MountInfo))
39 })
40 .try_fold(Vec::new(), |mut mount_points, r| {
41 r.map(|m| {
42 if m.fs_type == "cgroup" {
43 mount_points.push(m.mount_point);
44 }
45 mount_points
46 })
47 })
48}
49
50pub fn list_supported_mount_points() -> Result<HashMap<ControllerType, PathBuf>, V1MountPointError>
52{
53 let mut mount_paths = HashMap::with_capacity(CONTROLLERS.len());
54
55 for controller in CONTROLLERS {
56 if let Ok(mount_point) = get_subsystem_mount_point(controller) {
57 mount_paths.insert(controller.to_owned(), mount_point);
58 }
59 }
60
61 Ok(mount_paths)
62}
63
64pub fn get_subsystem_mount_point(subsystem: &ControllerType) -> Result<PathBuf, V1MountPointError> {
65 let subsystem_name = subsystem.to_string();
66 let reader = BufReader::new(ProcfsHandle::new()?.open(
67 ProcfsBase::ProcSelf,
68 "mountinfo",
69 OpenFlags::O_RDONLY | OpenFlags::O_CLOEXEC,
70 )?);
71
72 reader
73 .lines()
74 .map(|lr| {
75 lr.map_err(V1MountPointError::Io)
76 .and_then(|line| MountInfo::from_line(&line).map_err(V1MountPointError::MountInfo))
77 })
78 .find_map(|r| match r {
79 Err(e) => Some(Err(e)),
80 Ok(m) if m.fs_type == "cgroup" => {
81 let ok = match subsystem_name.as_str() {
85 "net_cls" => ["net_cls,net_prio", "net_prio,net_cls", "net_cls"]
86 .iter()
87 .any(|s| m.mount_point.ends_with(s)),
88 "net_prio" => ["net_cls,net_prio", "net_prio,net_cls", "net_prio"]
89 .iter()
90 .any(|s| m.mount_point.ends_with(s)),
91 "cpu" => ["cpu,cpuacct", "cpu"]
92 .iter()
93 .any(|s| m.mount_point.ends_with(s)),
94 "cpuacct" => ["cpu,cpuacct", "cpuacct"]
95 .iter()
96 .any(|s| m.mount_point.ends_with(s)),
97 _ => m.mount_point.ends_with(&subsystem_name),
98 };
99 if ok { Some(Ok(m.mount_point)) } else { None }
100 }
101 Ok(_) => None,
102 })
103 .transpose()?
104 .ok_or(V1MountPointError::NotFound {
105 subsystem: *subsystem,
106 })
107}