Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Mic92 committed Jun 1, 2016
0 parents commit 39da94d
Show file tree
Hide file tree
Showing 16 changed files with 970 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target/
117 changes: 117 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "cntr"
version = "0.1.0"
authors = ["Jörg Thalheim <joerg@higgsboson.tk>"]

[dependencies]
argparse = "0.2.*"
libc = "0.2.8"
log = "*"

[dependencies.nix]
git = "https://github.com/nix-rust/nix"
features = ["signalfd"]

[dependencies.fuse]
git = "https://github.com/zargony/rust-fuse"
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
all:
cargo build
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# cntr

A new container debugging tool

Status: WIP - nothing to see here yet
1 change: 1 addition & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
reorder_imports = true
110 changes: 110 additions & 0 deletions src/cgroup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use libc;
use types::{Error, Result};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::io::Write;
use std::collections::HashMap;
use std::path::PathBuf;

fn get_subsystems() -> Result<Vec<String>> {
let path = "/proc/cgroups";
let f = tryfmt!(File::open(&path), "failed to open /proc/cgroups");
let reader = BufReader::new(f);
let mut subsystems: Vec<String> = Vec::new();
for l in reader.lines() {
let line = tryfmt!(l, "failed to read /proc/cgroups");
if line.starts_with("#") {
continue;
}
let fields: Vec<&str> = line.split('\t').collect();
if fields.len() >= 4 && fields[3] != "0" {
subsystems.push(fields[0].to_string());
}
}
return Ok(subsystems);
}

fn get_mounts() -> Result<HashMap<String, String>> {
let subsystems = tryfmt!(get_subsystems(), "failed to obtain cgroup subsystems");
let path = format!("/proc/self/mountinfo");
// example:
//
// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
// (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
let f = tryfmt!(File::open(&path), "failed to read /proc/self/mountinfo");
let reader = BufReader::new(f);
let mut mountpoints: HashMap<String, String> = HashMap::new();
for l in reader.lines() {
let line = tryfmt!(l, "failed to read '{}'", path);
let fields: Vec<&str> = line.split(" ").collect();
if fields.len() < 11 || fields[9] != "cgroup" {
continue;
}
for option in fields[10].split(",") {
let name = if option.starts_with("name=") {
option[5..].to_string()
} else {
option.to_string()
};
if !subsystems.contains(&name) {
mountpoints.insert(name, fields[4].to_string());
}
}
}
return Ok(mountpoints);
}

fn get_cgroups(pid: libc::pid_t) -> Result<Vec<String>> {
let path = format!("/proc/{}/cgroup", pid);
let f = tryfmt!(File::open(&path), "failed to read {}", path);
let reader = BufReader::new(f);
let mut cgroups: Vec<String> = Vec::new();
for l in reader.lines() {
let line = tryfmt!(l, "failed to read '{}'", path);
let fields: Vec<&str> = line.split(":/").collect();
if fields.len() >= 2 {
cgroups.push(fields[1].to_string());
}
}
return Ok(cgroups);
}

fn cgroup_path(cgroup: &str, mountpoints: &HashMap<String, String>) -> Option<PathBuf> {
for c in cgroup.split(",") {
let m = mountpoints.get(c);
if m.is_some() {
let mut tasks_path = PathBuf::from(m.unwrap());
tasks_path.push(cgroup);
tasks_path.push("tasks");
return Some(tasks_path);
}
}
return None;
}

// TODO add implementation for unified cgroups, cgmanager, lxcfs
// -> on the long run everything will be done with unified cgroups hopefully

pub fn move_to(pid: libc::pid_t, target_pid: libc::pid_t) -> Result<()> {
let cgroups = tryfmt!(get_cgroups(target_pid),
"failed to get cgroups of {}",
target_pid);
let mountpoints = tryfmt!(get_mounts(), "failed to get cgroup mountpoints");
for cgroup in cgroups {
let p = cgroup_path(&cgroup, &mountpoints);
if p.is_some() {
let path = p.unwrap();
match File::create(&path) {
Ok(mut buffer) => {
tryfmt!(write!(buffer, "{}", pid),
"failed to enter {} cgroup",
cgroup);
}
Err(err) => {
warn!("failed to enter {} namespace: {}", cgroup, err);
}
}
}
}
return Ok(());
}
70 changes: 70 additions & 0 deletions src/cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use libc;
use nix::unistd;
use std::ffi::{CString, OsStr};
use std::path::PathBuf;
use std::fs::File;
use types::{Error, Result};
use std::io::{BufRead, BufReader};
use std::os::unix::ffi::OsStrExt;
use std::env;
use namespace;
use trace;

fn read_environ(pid: libc::pid_t) -> Result<Vec<CString>> {
let mut buf = PathBuf::from("/proc/");
buf.push(pid.to_string());
buf.push("environ");
let path = buf.as_path();
let f = tryfmt!(File::open(path),
"failed to open {}",
path.to_str().unwrap());
let reader = BufReader::new(f);
reader.split(b'\0')
.map(|var| {
let r = tryfmt!(var, "failed to read");
Ok(CString::new(r).unwrap())
})
.collect()
}

fn setns(pid: libc::pid_t) -> Result<()> {
let supported = tryfmt!(namespace::supported_namespaces(),
"can not get supported namespaces");
let mut namespaces = Vec::new();
for kind in supported {
if !kind.is_same(pid) {
namespaces.push(tryfmt!(kind.open(pid), "failed to open namespace"));
}
}
for ns in namespaces {
let name = &ns.kind.name;
tryfmt!(ns.apply(), "failed to apply {} namespace", name);
}
Ok(())
}

fn inherit_path(pid: libc::pid_t) -> Result<()> {
let env = tryfmt!(read_environ(pid),
"failed to get environment variables of target process {}",
pid);

let default_path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
let path = match env.iter().find(|var| var.as_bytes().starts_with(b"PATH=")) {
Some(n) => &n.as_bytes()[5..],
None => default_path.as_bytes(),
};
env::set_var("PATH", OsStr::from_bytes(&path));
Ok(())
}

pub fn exec(pid: libc::pid_t) -> Result<()> {
let arg0 = CString::new("/bin/sh").unwrap();
let arg1 = CString::new("-l").unwrap();
tryfmt!(setns(pid), "failed to enter namespace");

tryfmt!(trace::me(), "ptrace(PTRACE_TRACME) failed");
// Ok(tryfmt!(unistd::execvpe(&arg0, &[arg0.clone(), arg1], env.as_slice()),
// "failed to execute shell"))
Ok(tryfmt!(unistd::execvp(&arg0, &[arg0.clone(), arg1]),
"failed to execute shell"))
}
1 change: 1 addition & 0 deletions src/fuse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

22 changes: 22 additions & 0 deletions src/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use log;

struct Logger;

impl log::Log for Logger {
fn enabled(&self, metadata: &log::LogMetadata) -> bool {
metadata.level() <= log::LogLevel::Info
}

fn log(&self, record: &log::LogRecord) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());
}
}
}

pub fn init() -> Result<(), log::SetLoggerError> {
log::set_logger(|max_log_level| {
max_log_level.set(log::LogLevelFilter::Info);
Box::new(Logger)
})
}
Loading

0 comments on commit 39da94d

Please sign in to comment.