use eyre::{eyre, Context, Report, Result};
use procfs::process;
use std::fmt::Display;
const LIBC_NAME: &str = "libc.so.6";
#[derive(Debug)]
pub struct Process(process::Process);
impl Display for Process {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.pid)
}
}
impl Process {
pub(crate) fn current() -> Result<Self> {
Ok(Self(
process::Process::myself().wrap_err("failed to get PID of current process")?,
))
}
pub fn get(id: u32) -> Result<Self> {
let id = i32::try_from(id).expect("PID to fit in an i32");
log::trace!("Getting process with PID {}", id);
Ok(Self(
process::Process::new(id).wrap_err(format!("failed to get process by pid {id}"))?,
))
}
pub fn by_name(name: &str) -> Result<Option<Self>> {
log::debug!("Searching for process with executable name {}", name);
for process in
process::all_processes().wrap_err("failed to list processes to search them")?
{
let process = process.wrap_err("failed to read process metadata to check its name")?;
log::trace!("Checking process {}", process.pid);
if let Ok(exe) = process.exe() {
if exe.ends_with(name) {
log::info!("Found process with PID {}", process.pid);
return Ok(Some(Self(process)));
}
} else {
log::trace!("Could not read executable name of process {}", process.pid);
}
}
Ok(None)
}
pub(crate) fn find_executable_space(&self) -> Result<u64> {
log::trace!("Finding executable space in target process");
self.0
.maps()
.wrap_err("failed to read process memory maps to find executable region")?
.into_iter()
.find(|m| m.perms.contains(process::MMPermissions::EXECUTE))
.map(|m| m.address.0)
.ok_or(eyre!(
"could not find an executable region in the target process"
))
}
pub(crate) fn libc_address(&self) -> Result<u64> {
let library = std::path::PathBuf::from(LIBC_NAME);
log::trace!("Finding libc address in process with PID {}", self.0.pid);
self.0
.maps()
.wrap_err("failed to read process memory maps to find libc")?
.into_iter()
.find(move |m| {
log::trace!("Checking mapping: {m:?}");
match &m.pathname {
process::MMapPath::Path(path) => path.ends_with(&library),
_ => false,
}
})
.map(|m| m.address.0)
.ok_or(eyre!("could not find libc in the target process"))
}
pub(crate) fn thread_ids(&self) -> Result<Vec<i32>> {
log::trace!("Getting thread IDs of process with PID {}", self.0.pid);
self.0
.tasks()
.wrap_err("failed to read process thread IDs")?
.map(|t| Ok::<_, Report>(t.wrap_err("failed to read process thread IDs")?.tid))
.collect()
}
pub(crate) const fn pid(&self) -> i32 {
self.0.pid
}
}
impl From<&Process> for pete::Pid {
fn from(proc: &Process) -> Self {
Self::from_raw(proc.0.pid)
}
}