use std::cmp;
use std::convert::TryFrom;
use std::ffi::CStr;
use std::hash;
use std::io;
use std::path::PathBuf;
use futures::future::BoxFuture;
use heim_common::prelude::*;
use heim_common::sys::IntoTime;
use heim_common::units::Time;
use super::{bindings, pids, utils::catch_zombie};
use crate::os::unix::Signal;
use crate::sys::common::UniqueId;
use crate::sys::unix::pid_kill;
use crate::{Pid, ProcessError, ProcessResult, Status};
mod command;
mod cpu_times;
mod memory;
pub use self::command::{Command, CommandIter};
pub use self::cpu_times::CpuTime;
pub use self::memory::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>> {
match bindings::process(self.pid) {
Ok(kinfo_proc) => future::ok(kinfo_proc.kp_eproc.e_ppid),
Err(e) => future::err(catch_zombie(e, self.pid)),
}
}
pub fn name(&self) -> impl Future<Output = ProcessResult<String>> {
match bindings::process(self.pid) {
Ok(kinfo_proc) => {
let raw_str = unsafe { CStr::from_ptr(kinfo_proc.kp_proc.p_comm.as_ptr()) };
let name = raw_str.to_string_lossy().into_owned();
future::ok(name)
}
Err(e) => future::err(catch_zombie(e, self.pid)),
}
}
pub fn exe(&self) -> impl Future<Output = ProcessResult<PathBuf>> {
match darwin_libproc::pid_path(self.pid) {
Ok(path) => future::ok(path),
Err(..) if self.pid == 0 => future::err(ProcessError::AccessDenied(self.pid)),
Err(e) => future::err(catch_zombie(e, self.pid)),
}
}
pub fn command(&self) -> impl Future<Output = ProcessResult<Command>> {
self::command::command(self.pid)
}
pub fn cwd(&self) -> impl Future<Output = ProcessResult<PathBuf>> {
match darwin_libproc::pid_cwd(self.pid) {
Ok(path) => future::ok(path),
Err(e) => future::err(catch_zombie(e, self.pid)),
}
}
pub fn status(&self) -> impl Future<Output = ProcessResult<Status>> {
match bindings::process(self.pid) {
Ok(kinfo_proc) => {
future::ready(Status::try_from(kinfo_proc.kp_proc.p_stat).map_err(From::from))
}
Err(e) => future::err(catch_zombie(e, self.pid)),
}
}
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>> {
match darwin_libproc::task_info(self.pid) {
Ok(task_info) => future::ok(CpuTime::from(task_info)),
Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {
future::err(ProcessError::AccessDenied(self.pid))
}
Err(e) => future::err(catch_zombie(e, self.pid)),
}
}
pub fn memory(&self) -> impl Future<Output = ProcessResult<Memory>> {
match darwin_libproc::task_info(self.pid) {
Ok(task_info) => future::ok(Memory::from(task_info)),
Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {
future::err(ProcessError::AccessDenied(self.pid))
}
Err(e) => future::err(catch_zombie(e, 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)
}
}
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>> {
match bindings::process(pid) {
Ok(kinfo_proc) => {
let create_time = unsafe {
kinfo_proc.kp_proc.p_un.p_starttime
};
let create_time = create_time.into_time();
debug_assert!(!create_time.is_nan());
future::ok(Process {
pid,
unique_id: UniqueId::new(pid, create_time),
})
}
Err(e) => future::err(catch_zombie(e, pid)),
}
}
pub fn current() -> impl Future<Output = ProcessResult<Process>> {
future::lazy(|_| unsafe { libc::getpid() }).then(get)
}