use std::cmp;
use std::hash;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use heim_common::prelude::*;
use heim_common::units::Time;
use heim_runtime::fs;
use super::{pid_exists, pids};
use crate::os::linux::IoCounters;
use crate::os::unix::Signal;
use crate::sys::common::UniqueId;
use crate::sys::unix::pid_kill;
use crate::{Pid, ProcessError, ProcessResult, Status};
mod procfs;
pub use self::procfs::{Command, CommandIter, CpuTime, Memory};
#[derive(Debug)]
pub struct Process {
pid: Pid,
unique_id: UniqueId,
}
impl Process {
pub fn pid(&self) -> Pid {
self.pid
}
pub fn parent_pid(&self) -> impl Future<Output = ProcessResult<Pid>> {
procfs::stat(self.pid).map_ok(|procfs::Stat { ppid, .. }| ppid)
}
pub fn name(&self) -> impl Future<Output = ProcessResult<String>> {
let pid = self.pid;
procfs::stat(pid)
.map_ok(|procfs::Stat { name, .. }| name)
.and_then(move |name| {
if name.len() >= 15 {
let orig_name = name.clone();
let f = procfs::command(pid)
.and_then(move |command| {
let path = command
.into_iter()
.next()
.map(Path::new)
.and_then(Path::file_name);
match path {
Some(exe) if exe.as_bytes().starts_with(name.as_bytes()) => {
future::ok(exe.to_string_lossy().into_owned())
}
_ => future::ok(name),
}
})
.or_else(move |_| future::ok(orig_name));
future::Either::Left(f)
} else {
future::Either::Right(future::ok(name))
}
})
}
pub fn exe(&self) -> impl Future<Output = ProcessResult<PathBuf>> {
let pid = self.pid;
fs::read_link(format!("/proc/{}/exe", self.pid)).or_else(move |_| {
pid_exists(pid).and_then(move |exists| {
if exists {
future::ok(PathBuf::new())
} else {
future::err(ProcessError::ZombieProcess(pid))
}
})
})
}
pub fn command(&self) -> impl Future<Output = ProcessResult<Command>> {
let pid = self.pid;
self::procfs::command(pid)
}
pub fn cwd(&self) -> impl Future<Output = ProcessResult<PathBuf>> {
let pid = self.pid;
fs::read_link(format!("/proc/{}/cwd", self.pid)).or_else(move |_| {
pid_exists(pid).and_then(move |exists| {
if exists {
future::err(ProcessError::ZombieProcess(pid))
} else {
future::err(ProcessError::AccessDenied(pid))
}
})
})
}
pub fn status(&self) -> impl Future<Output = ProcessResult<Status>> {
procfs::stat(self.pid).map_ok(|procfs::Stat { state, .. }| state)
}
pub fn create_time(&self) -> impl Future<Output = ProcessResult<Time>> {
future::ok(self.unique_id.create_time())
}
pub fn cpu_time(&self) -> impl Future<Output = ProcessResult<CpuTime>> {
procfs::stat(self.pid).map_ok(Into::into)
}
pub fn memory(&self) -> impl Future<Output = ProcessResult<Memory>> {
procfs::stat_memory(self.pid)
}
pub fn is_running(&self) -> impl Future<Output = ProcessResult<bool>> {
let unique_id = self.unique_id.clone();
get(self.pid).map_ok(move |other| other.unique_id == unique_id)
}
fn _signal(&self, signal: Signal) -> impl Future<Output = ProcessResult<()>> {
let pid = self.pid;
self.is_running().and_then(move |is_running| {
if is_running {
future::ready(pid_kill(pid, signal))
} else {
future::err(ProcessError::NoSuchProcess(pid))
}
})
}
pub fn signal(&self, signal: Signal) -> BoxFuture<ProcessResult<()>> {
self._signal(signal).boxed()
}
pub fn suspend(&self) -> impl Future<Output = ProcessResult<()>> {
self._signal(Signal::Stop)
}
pub fn resume(&self) -> impl Future<Output = ProcessResult<()>> {
self._signal(Signal::Cont)
}
pub fn terminate(&self) -> impl Future<Output = ProcessResult<()>> {
self._signal(Signal::Term)
}
pub fn kill(&self) -> impl Future<Output = ProcessResult<()>> {
self._signal(Signal::Kill)
}
pub fn io_counters(&self) -> BoxFuture<ProcessResult<IoCounters>> {
procfs::io(self.pid).boxed()
}
pub fn net_io_counters(&self) -> BoxStream<ProcessResult<heim_net::IoCounters>> {
heim_net::os::linux::io_counters_for_pid(self.pid())
.map_err(Into::into)
.boxed()
}
}
impl hash::Hash for Process {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.unique_id.hash(state);
}
}
impl cmp::PartialEq for Process {
fn eq(&self, other: &Self) -> bool {
self.unique_id == other.unique_id
}
}
impl cmp::Eq for Process {}
pub fn processes() -> impl Stream<Item = ProcessResult<Process>> {
pids().map_err(Into::into).and_then(get)
}
pub fn get(pid: Pid) -> impl Future<Output = ProcessResult<Process>> {
procfs::stat(pid).map_ok(move |procfs::Stat { create_time, .. }| Process {
pid,
unique_id: UniqueId::new(pid, create_time),
})
}
pub fn current() -> impl Future<Output = ProcessResult<Process>> {
future::lazy(|_| unsafe { libc::getpid() }).then(get)
}