-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 39da94d
Showing
16 changed files
with
970 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/target/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
all: | ||
cargo build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
reorder_imports = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) | ||
} |
Oops, something went wrong.