use crate::api::{Args, ArgsBuilder, Response};
use crate::ctrl::tracer::CtrlTracer;
use crate::ctrl::ReqNewClient;
use crate::ctx;
use crate::trace::ptrace::Tracer;
use crate::utils::process::Process;
use crate::Result;
use std::thread::JoinHandle;
pub struct Main<T, Err>
where
Err: Into<crate::Error>,
{
ctx: ctx::Secondary<T, Err>,
handle: JoinHandle<Result<()>>,
}
impl<T, Err> Main<T, Err>
where
Err: Into<crate::Error>,
{
fn new(ctx: ctx::Secondary<T, Err>, handle: JoinHandle<Result<()>>) -> Self {
Self { ctx, handle }
}
pub fn new_main(attach: bool, prog: String, args: Vec<String>, state: T) -> Result<Self> {
let ctx = if attach {
if !args.is_empty() {
log::warn!("extra args '{args:?}' will be ignored");
}
let pid = Process::arg_to_pid(&prog)?;
let proc = Process::from_pid(pid as u32)?;
Self::new_attach(proc, state)?
} else {
let mut cmd = std::process::Command::new(prog);
cmd.args(args);
Self::new_spawn(cmd, state)?
};
Ok(ctx)
}
#[cfg(not(target_arch = "arm"))]
pub fn spawn_in_mem<S: Into<String>, V: Into<Vec<String>>>(
name: S, elf: Vec<u8>, args: V, data: T,
) -> Result<Self> {
let name: String = name.into();
let args: Vec<String> = args.into();
let (req, acc) = ReqNewClient::new();
let nelf = elf.clone();
let handle = std::thread::spawn(move || -> Result<()> {
let (tracer, _tid) = Tracer::spawn_in_mem(&name, nelf, args)?;
let mgr = CtrlTracer::new(tracer, acc)?;
mgr.scheduler()?;
Ok(())
});
let client = req.new_regular()?;
log::info!("ready to send commands to target");
let mut ctx = ctx::Secondary::new_master(client, data, req)?;
let args = ArgsBuilder::new().handle_exec();
ctx.set_args_builder(args);
let tid = ctx.get_first_stopped()?;
let old = ctx.run_until_exec()?;
assert!(tid == old);
let mainloc = ctx
.proc
.exact_match_path("/memfd:rust_exec (deleted)")?
.expect("unable to location of main binary");
ctx.set_main_exe(mainloc.addr(), elf)?;
ctx.set_args_builder(ArgsBuilder::new_dirty());
Ok(Self::new(ctx, handle))
}
pub fn secondary(&self) -> &ctx::Secondary<T, Err> {
&self.ctx
}
pub fn secondary_mut(&mut self) -> &mut ctx::Secondary<T, Err> {
&mut self.ctx
}
pub fn new_attach_pid(pid: u32, data: T) -> Result<Self> {
let proc = Process::from_pid(pid)?;
Self::new_attach(proc, data)
}
pub fn new_attach_procname(name: &str, data: T) -> Result<Self> {
let proc = Process::procname_to_process(name)?;
Self::new_attach(proc, data)
}
pub fn new_spawn(cmd: std::process::Command, data: T) -> Result<Self> {
let (req, acc) = ReqNewClient::new();
let handle = std::thread::spawn(move || -> Result<()> {
let tracer = Tracer::spawn(cmd)?;
let mgr = CtrlTracer::new(tracer, acc)?;
mgr.scheduler()?;
Ok(())
});
let client = req.new_regular()?;
log::info!("ready to send commands to target");
let ctx = ctx::Secondary::new_master(client, data, req)?;
Ok(Self::new(ctx, handle))
}
pub fn loop_until_exit(mut self) -> Result<(Response, T)> {
log::info!("looping until exit");
let rsp = self.ctx.loop_until_exit()?;
let (t, err) = self.join()?;
if !err.is_empty() {
let err = err.join(" | ");
Err(crate::Error::msg(format!("errors: {err}")))
} else {
Ok((rsp, t))
}
}
pub fn detach(mut self) -> Result<(T, Vec<String>)> {
self.ctx.client_mut().detach()?;
let t = self.join()?;
Ok(t)
}
pub fn join(self) -> Result<(T, Vec<String>)> {
log::info!("joining all threads");
let mut errs = Vec::new();
#[cfg(feature = "plugins")]
for (key, plugin) in self.ctx.plugins.into_iter() {
log::debug!("witing for plugin {key:?}");
let done = plugin.handle.join();
if let Err(e) = done {
let err = format!("plugin thread {key} failed: {e:?}");
errs.push(err);
}
}
if let Err(e) = self.handle.join() {
let err = format!("thread handle failed: {e:?}");
errs.push(err);
}
Ok((self.ctx.data, errs))
}
pub fn new_attach(proc: Process, data: T) -> Result<Self> {
let (req, acc) = ReqNewClient::new();
let handle = std::thread::spawn(move || -> Result<()> {
let tracer = Tracer::attach(proc)?;
let mgr = CtrlTracer::new(tracer, acc)?;
mgr.scheduler()?;
Ok(())
});
let client = req.new_regular()?;
log::info!("ready to send commands to target");
let ctx = ctx::Secondary::new_master(client, data, req)?;
Ok(Self::new(ctx, handle))
}
}