debug helper
This commit is contained in:
parent
ff32b48633
commit
a6bf4871f4
14
README.md
14
README.md
@ -21,3 +21,17 @@ To run this example:
|
||||
cargo build
|
||||
cargo build --example pipes
|
||||
sudo target/debug/clone-shim -s examples/pipes/spec.json target/debug/examples/pipes
|
||||
|
||||
## Debugging the shim
|
||||
|
||||
The shim can be debugged as with most processes, but it is exceptionally forky. Breaking before a clone in `rust-gdb` then running `set follow-fork-mode child` is often necessary. The best approach is to go in with a plan of attack.
|
||||
|
||||
## Debugging the child
|
||||
|
||||
Debugging the child processes is vastly more difficult than in other more Linux-like containerisation solutions.
|
||||
|
||||
The `--debug` flag on the shim attempts to stop application spawned processes as soon as they are voided. This gives you a chance to attach with a debugger.
|
||||
|
||||
The debugger must be run from the ambient namespace and not within the void, as none of the prerequisites will exist within the void.
|
||||
|
||||
Good luck!
|
||||
|
@ -28,6 +28,7 @@ pub fn run() -> Result<()> {
|
||||
.arg(clap::Arg::new("spec").long("specification").short('s').help("Provide the specification as an external JSON file.").takes_value(true))
|
||||
.setting(AppSettings::TrailingVarArg)
|
||||
.arg(clap::Arg::new("verbose").long("verbose").short('v').help("Use verbose logging.").takes_value(false))
|
||||
.arg(clap::Arg::new("debug").long("debug").short('d').help("Stop each spawned application process so that it can be attached to.").takes_value(false))
|
||||
.arg(clap::Arg::new("binary").index(1).help("Binary and arguments to launch with the shim").required(true).multiple_values(true))
|
||||
.get_matches();
|
||||
|
||||
@ -74,15 +75,20 @@ pub fn run() -> Result<()> {
|
||||
let sockets = create_sockets(sockets)?;
|
||||
|
||||
// spawn all processes
|
||||
let debug = matches.is_present("debug");
|
||||
|
||||
Spawner {
|
||||
spec: &spec,
|
||||
binary,
|
||||
trailing: &trailing,
|
||||
debug,
|
||||
|
||||
pipes,
|
||||
sockets,
|
||||
}
|
||||
.spawn()
|
||||
.spawn()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_pipes(names: Vec<&str>) -> Result<HashMap<String, PipePair>> {
|
||||
|
@ -16,8 +16,9 @@ use std::io::Read;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use nix::sys::signal::{kill, Signal};
|
||||
use nix::sys::socket::{recvmsg, ControlMessageOwned, MsgFlags};
|
||||
use nix::unistd;
|
||||
use nix::unistd::{self, Pid};
|
||||
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
|
||||
@ -25,6 +26,7 @@ pub struct Spawner<'a> {
|
||||
pub spec: &'a Specification,
|
||||
pub binary: &'a str,
|
||||
pub trailing: &'a Vec<&'a str>,
|
||||
pub debug: bool,
|
||||
|
||||
pub pipes: HashMap<String, PipePair>,
|
||||
pub sockets: HashMap<String, SocketPair>,
|
||||
@ -72,6 +74,10 @@ impl<'a> Spawner<'a> {
|
||||
PreparedArgs::prepare_ambient_mut(self, &mut builder, &entrypoint.args)?;
|
||||
|
||||
let closure = || {
|
||||
if self.debug {
|
||||
Self::stop_self(name).unwrap()
|
||||
}
|
||||
|
||||
let args = args
|
||||
.prepare_void(self, name, &mut TriggerData::None)
|
||||
.unwrap();
|
||||
@ -89,7 +95,8 @@ impl<'a> Spawner<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
builder.spawn(closure)?;
|
||||
let void = builder.spawn(closure)?;
|
||||
info!("spawned entrypoint `{}` as {}", name.as_str(), void);
|
||||
}
|
||||
|
||||
Trigger::Pipe(s) => {
|
||||
@ -110,7 +117,12 @@ impl<'a> Spawner<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
builder.spawn(closure)?;
|
||||
let void = builder.spawn(closure)?;
|
||||
info!(
|
||||
"prepared pipe trigger for entrypoint `{}` as {}",
|
||||
name.as_str(),
|
||||
void
|
||||
);
|
||||
}
|
||||
|
||||
Trigger::FileSocket(s) => {
|
||||
@ -131,7 +143,12 @@ impl<'a> Spawner<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
builder.spawn(closure)?;
|
||||
let void = builder.spawn(closure)?;
|
||||
info!(
|
||||
"prepared socket trigger for entrypoint `{}` as {}",
|
||||
name.as_str(),
|
||||
void
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,6 +184,10 @@ impl<'a> Spawner<'a> {
|
||||
|
||||
let closure =
|
||||
|| {
|
||||
if self.debug {
|
||||
Self::stop_self(name).unwrap()
|
||||
}
|
||||
|
||||
let pipe_trigger = std::str::from_utf8(&buf[0..read_bytes]).unwrap();
|
||||
|
||||
let args = args
|
||||
@ -230,6 +251,10 @@ impl<'a> Spawner<'a> {
|
||||
let args = PreparedArgs::prepare_ambient(&mut builder, &spec.args)?;
|
||||
|
||||
let closure = || {
|
||||
if self.debug {
|
||||
Self::stop_self(name).unwrap()
|
||||
}
|
||||
|
||||
let args = args
|
||||
.prepare_void(self, name, &mut TriggerData::FileSocket(fds))
|
||||
.unwrap();
|
||||
@ -257,6 +282,19 @@ impl<'a> Spawner<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_self(name: &str) -> Result<()> {
|
||||
let pid = Pid::this();
|
||||
info!("stopping process `{}`", name);
|
||||
|
||||
kill(pid, Signal::SIGSTOP).map_err(|e| Error::Nix {
|
||||
msg: "kill",
|
||||
src: e,
|
||||
})?;
|
||||
|
||||
info!("process `{}` resumed", name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_env<'b>(
|
||||
&self,
|
||||
builder: &mut VoidBuilder,
|
||||
|
14
src/void.rs
14
src/void.rs
@ -4,9 +4,9 @@ use crate::clone::{clone3, CloneArgs, CloneFlags};
|
||||
use crate::{Error, Result};
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{fmt, fs};
|
||||
|
||||
use nix::fcntl::{FcntlArg, FdFlag};
|
||||
use nix::mount::{mount, umount2, MntFlags, MsFlags};
|
||||
@ -16,7 +16,15 @@ use nix::unistd::{pivot_root, Pid};
|
||||
|
||||
use close_fds::CloseFdsBuilder;
|
||||
|
||||
pub struct VoidHandle {}
|
||||
pub struct VoidHandle {
|
||||
pid: Pid,
|
||||
}
|
||||
|
||||
impl fmt::Display for VoidHandle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Void{{Pid:{}}}", self.pid)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VoidBuilder {
|
||||
mounts: HashMap<PathBuf, PathBuf>,
|
||||
@ -90,7 +98,7 @@ impl VoidBuilder {
|
||||
// be a problem.
|
||||
std::mem::forget(child_fn);
|
||||
|
||||
Ok(VoidHandle {})
|
||||
Ok(VoidHandle { pid: child })
|
||||
}
|
||||
|
||||
// per-namespace void creation
|
||||
|
Loading…
Reference in New Issue
Block a user