mirror of
https://github.com/wez/wezterm.git
synced 2024-12-29 00:21:57 +03:00
213 lines
7.0 KiB
Rust
213 lines
7.0 KiB
Rust
#![cfg(target_os = "macos")]
|
|
use super::*;
|
|
|
|
impl From<u32> for LocalProcessStatus {
|
|
fn from(s: u32) -> Self {
|
|
match s {
|
|
1 => Self::Idle,
|
|
2 => Self::Run,
|
|
3 => Self::Sleep,
|
|
4 => Self::Stop,
|
|
5 => Self::Zombie,
|
|
_ => Self::Unknown,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LocalProcessInfo {
|
|
pub fn with_root_pid(pid: u32) -> Option<Self> {
|
|
/// Enumerate all current process identifiers
|
|
fn all_pids() -> Vec<libc::pid_t> {
|
|
let num_pids = unsafe { libc::proc_listallpids(std::ptr::null_mut(), 0) };
|
|
if num_pids < 1 {
|
|
return vec![];
|
|
}
|
|
|
|
// Give a bit of padding to avoid looping if processes are spawning
|
|
// rapidly while we're trying to collect this info
|
|
const PADDING: usize = 32;
|
|
let mut pids: Vec<libc::pid_t> = Vec::with_capacity(num_pids as usize + PADDING);
|
|
loop {
|
|
let n = unsafe {
|
|
libc::proc_listallpids(
|
|
pids.as_mut_ptr() as *mut _,
|
|
(pids.capacity() * std::mem::size_of::<libc::pid_t>()) as _,
|
|
)
|
|
};
|
|
|
|
if n < 1 {
|
|
return vec![];
|
|
}
|
|
|
|
let n = n as usize;
|
|
|
|
if n > pids.capacity() {
|
|
pids.reserve(n + PADDING);
|
|
continue;
|
|
}
|
|
|
|
unsafe { pids.set_len(n) };
|
|
return pids;
|
|
}
|
|
}
|
|
|
|
/// Obtain info block for a pid.
|
|
/// Note that the process could have gone away since we first
|
|
/// observed the pid and the time we call this, so we must
|
|
/// be able to tolerate this failing.
|
|
fn info_for_pid(pid: libc::pid_t) -> Option<libc::proc_bsdinfo> {
|
|
let mut info: libc::proc_bsdinfo = unsafe { std::mem::zeroed() };
|
|
let wanted_size = std::mem::size_of::<libc::proc_bsdinfo>() as _;
|
|
let res = unsafe {
|
|
libc::proc_pidinfo(
|
|
pid,
|
|
libc::PROC_PIDTBSDINFO,
|
|
0,
|
|
&mut info as *mut _ as *mut _,
|
|
wanted_size,
|
|
)
|
|
};
|
|
|
|
if res == wanted_size {
|
|
Some(info)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn cwd_for_pid(pid: libc::pid_t) -> PathBuf {
|
|
let mut pathinfo: libc::proc_vnodepathinfo = unsafe { std::mem::zeroed() };
|
|
let size = std::mem::size_of_val(&pathinfo) as libc::c_int;
|
|
let ret = unsafe {
|
|
libc::proc_pidinfo(
|
|
pid,
|
|
libc::PROC_PIDVNODEPATHINFO,
|
|
0,
|
|
&mut pathinfo as *mut _ as *mut _,
|
|
size,
|
|
)
|
|
};
|
|
if ret == size {
|
|
let path =
|
|
unsafe { std::ffi::CStr::from_ptr(pathinfo.pvi_cdir.vip_path.as_ptr() as _) };
|
|
path.to_str().unwrap_or("").into()
|
|
} else {
|
|
PathBuf::new()
|
|
}
|
|
}
|
|
|
|
fn exe_and_args_for_pid_sysctl(pid: libc::pid_t) -> Option<(PathBuf, Vec<String>)> {
|
|
use libc::c_int;
|
|
let mut size = 64 * 1024;
|
|
let mut buf: Vec<u8> = Vec::with_capacity(size);
|
|
let mut mib = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid as c_int];
|
|
|
|
let res = unsafe {
|
|
libc::sysctl(
|
|
mib.as_mut_ptr(),
|
|
mib.len() as _,
|
|
buf.as_mut_ptr() as *mut _,
|
|
&mut size,
|
|
std::ptr::null_mut(),
|
|
0,
|
|
)
|
|
};
|
|
if res == -1 {
|
|
return None;
|
|
}
|
|
if size < (std::mem::size_of::<c_int>() * 2) {
|
|
// Not big enough
|
|
return None;
|
|
}
|
|
unsafe { buf.set_len(size) };
|
|
|
|
// The data in our buffer is laid out like this:
|
|
// argc - c_int
|
|
// exe_path - NUL terminated string
|
|
// argv[0] - NUL terminated string
|
|
// argv[1] - NUL terminated string
|
|
// ...
|
|
// argv[n] - NUL terminated string
|
|
// envp[0] - NUL terminated string
|
|
// ...
|
|
|
|
let mut ptr = &buf[0..size];
|
|
|
|
let argc: c_int = unsafe { std::ptr::read(ptr.as_ptr() as *const c_int) };
|
|
ptr = &ptr[std::mem::size_of::<c_int>()..];
|
|
|
|
fn consume_cstr(ptr: &mut &[u8]) -> Option<String> {
|
|
let nul = ptr.iter().position(|&c| c == 0)?;
|
|
let s = String::from_utf8_lossy(&ptr[0..nul]).to_owned().to_string();
|
|
*ptr = ptr.get(nul + 1..)?;
|
|
Some(s)
|
|
}
|
|
|
|
let exe_path = consume_cstr(&mut ptr)?.into();
|
|
|
|
let mut args = vec![];
|
|
for _ in 0..argc {
|
|
args.push(consume_cstr(&mut ptr)?);
|
|
}
|
|
|
|
Some((exe_path, args))
|
|
}
|
|
|
|
fn exe_for_pid(pid: libc::pid_t) -> PathBuf {
|
|
let mut buffer: Vec<u8> = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _);
|
|
let x = unsafe {
|
|
libc::proc_pidpath(
|
|
pid,
|
|
buffer.as_mut_ptr() as *mut _,
|
|
libc::PROC_PIDPATHINFO_MAXSIZE as _,
|
|
)
|
|
};
|
|
if x > 0 {
|
|
unsafe { buffer.set_len(x as usize) };
|
|
String::from_utf8_lossy(&buffer)
|
|
.to_owned()
|
|
.to_string()
|
|
.into()
|
|
} else {
|
|
PathBuf::new()
|
|
}
|
|
}
|
|
|
|
let procs: Vec<_> = all_pids().into_iter().filter_map(info_for_pid).collect();
|
|
|
|
fn build_proc(info: &libc::proc_bsdinfo, procs: &[libc::proc_bsdinfo]) -> LocalProcessInfo {
|
|
let mut children = HashMap::new();
|
|
|
|
for kid in procs {
|
|
if kid.pbi_ppid == info.pbi_pid {
|
|
children.insert(kid.pbi_pid, build_proc(kid, procs));
|
|
}
|
|
}
|
|
|
|
let (executable, argv) = exe_and_args_for_pid_sysctl(info.pbi_pid as _)
|
|
.unwrap_or_else(|| (exe_for_pid(info.pbi_pid as _), vec![]));
|
|
|
|
let name = unsafe { std::ffi::CStr::from_ptr(info.pbi_comm.as_ptr() as _) };
|
|
let name = name.to_str().unwrap_or("").to_string();
|
|
|
|
LocalProcessInfo {
|
|
pid: info.pbi_pid,
|
|
ppid: info.pbi_ppid,
|
|
name,
|
|
executable,
|
|
cwd: cwd_for_pid(info.pbi_pid as _),
|
|
argv,
|
|
start_time: info.pbi_start_tvsec,
|
|
status: LocalProcessStatus::from(info.pbi_status),
|
|
children,
|
|
}
|
|
}
|
|
|
|
if let Some(info) = procs.iter().find(|info| info.pbi_pid == pid) {
|
|
Some(build_proc(info, &procs))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|