cntr 2.0.0

A container debugging tool based on Linux mount API
Documentation
use anyhow::Context;
use nix::sched;
use nix::unistd;
use std::collections::HashSet;
use std::fs::{self, File};
use std::os::unix::prelude::*;
use std::path::PathBuf;

use crate::procfs;
use crate::result::Result;

pub(crate) const MOUNT: Kind = Kind { name: "mnt" };
pub(crate) const UTS: Kind = Kind { name: "uts" };
pub(crate) const USER: Kind = Kind { name: "user" };
pub(crate) const PID: Kind = Kind { name: "pid" };
pub(crate) const NET: Kind = Kind { name: "net" };
pub(crate) const CGROUP: Kind = Kind { name: "cgroup" };
pub(crate) const IPC: Kind = Kind { name: "ipc" };

pub(crate) struct Kind {
    pub(crate) name: &'static str,
}

pub(crate) fn supported_namespaces() -> Result<HashSet<String>> {
    let mut namespaces = HashSet::new();
    let entries = fs::read_dir(PathBuf::from("/proc/self/ns"))
        .context("failed to open directory /proc/self/ns")?;
    for entry in entries {
        let entry = entry.context("failed to read directory entry in /proc/self/ns")?;
        if let Ok(name) = entry.file_name().into_string() {
            namespaces.insert(name);
        }
    }
    Ok(namespaces)
}

impl Kind {
    pub(crate) fn open(&'static self, pid: unistd::Pid) -> Result<Namespace> {
        let buf = self.path(pid);
        let file = File::open(&buf)
            .with_context(|| format!("failed to open namespace file '{}'", buf.display()))?;
        Ok(Namespace { kind: self, file })
    }

    pub(crate) fn is_same(&self, pid: unistd::Pid) -> bool {
        let path = self.path(pid);
        match fs::read_link(path) {
            Ok(dest) => match fs::read_link(self.own_path()) {
                Ok(dest2) => dest == dest2,
                _ => false,
            },
            _ => false,
        }
    }
    fn path(&self, pid: unistd::Pid) -> PathBuf {
        procfs::get_path()
            .join(pid.to_string())
            .join("ns")
            .join(self.name)
    }

    fn own_path(&self) -> PathBuf {
        PathBuf::from("/proc/self/ns").join(self.name)
    }
}

pub(crate) struct Namespace {
    pub(crate) kind: &'static Kind,
    file: File,
}

impl Namespace {
    pub(crate) fn apply(&self) -> Result<()> {
        sched::setns(self.file.as_fd(), sched::CloneFlags::empty())
            .with_context(|| format!("failed to set namespace '{}'", self.kind.name))?;
        Ok(())
    }
}