1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-20 03:09:06 +03:00

mux: remove sysinfo dep on macos

We can get just the targeted information we need a bit more robustly
and cheaply.
This commit is contained in:
Wez Furlong 2021-12-31 14:54:03 -07:00
parent 80d16574e3
commit efbcdc860e
4 changed files with 213 additions and 7 deletions

View File

@ -38,7 +38,7 @@ url = "2"
wezterm-ssh = { path = "../wezterm-ssh" }
wezterm-term = { path = "../term", features=["use_serde"] }
[target.'cfg(any(windows, target_os="linux", target_os="macos"))'.dependencies]
[target.'cfg(any(windows, target_os="linux"))'.dependencies]
sysinfo = "0.22"
[target."cfg(windows)".dependencies]

View File

@ -838,11 +838,16 @@ impl LocalPane {
None
}
fn divine_process_list(&self, force_refresh: bool) -> Option<LocalProcessInfo> {
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
fn divine_process_list(&self, _force_refresh: bool) -> Option<LocalProcessInfo> {
#[cfg(target_os = "macos")]
if let ProcessState::Running { pid: Some(pid), .. } = &*self.process.borrow() {
return dbg!(LocalProcessInfo::with_root_pid_macos(*pid));
}
#[cfg(any(windows, target_os = "linux"))]
if let ProcessState::Running { pid: Some(pid), .. } = &*self.process.borrow() {
return LocalProcessInfo::with_root_pid(
&*if force_refresh {
&*if _force_refresh {
crate::sysinfo::get_with_forced_refresh()
} else {
crate::sysinfo::get()

View File

@ -18,7 +18,7 @@ pub enum LocalProcessStatus {
Unknown,
}
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
#[cfg(any(windows, target_os = "linux"))]
impl LocalProcessStatus {
fn from_process_status(status: sysinfo::ProcessStatus) -> Self {
match status {
@ -70,7 +70,208 @@ impl LocalProcessInfo {
}
}
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
#[cfg(target_os = "macos")]
impl LocalProcessInfo {
pub(crate) fn with_root_pid_macos(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 = executable
.file_name()
.and_then(|e| e.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::Idle,
children,
}
}
if let Some(info) = procs.iter().find(|info| info.pbi_pid == pid) {
Some(build_proc(info, &procs))
} else {
None
}
}
}
#[cfg(any(windows, target_os = "linux"))]
impl LocalProcessInfo {
pub(crate) fn with_root_pid(system: &sysinfo::System, pid: u32) -> Option<Self> {
use sysinfo::{AsU32, Pid, Process, ProcessExt, SystemExt};

View File

@ -1,4 +1,4 @@
#![cfg(any(windows, target_os = "linux", target_os = "macos"))]
#![cfg(any(windows, target_os = "linux"))]
use std::sync::{Mutex, MutexGuard};
use std::time::{Duration, Instant};
use sysinfo::{ProcessRefreshKind, RefreshKind, System, SystemExt};