From 80d16574e3e3654dd5017bd512be14c139a0f681 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Fri, 31 Dec 2021 11:30:10 -0700 Subject: [PATCH] mux: refactor sysinfo. Use own fn for macos foreground process path I noticed that sysinfo failed to yield info about 50% of the time on macos! Just go direct to the underlying system function; we don't need all of the info that sysinfo collects in any case. --- mux/src/lib.rs | 1 + mux/src/localpane.rs | 130 ++++++++----------------------------------- mux/src/procinfo.rs | 108 +++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 108 deletions(-) create mode 100644 mux/src/procinfo.rs diff --git a/mux/src/lib.rs b/mux/src/lib.rs index 77ce4c9ad..406d11c87 100644 --- a/mux/src/lib.rs +++ b/mux/src/lib.rs @@ -29,6 +29,7 @@ pub mod connui; pub mod domain; pub mod localpane; pub mod pane; +pub mod procinfo; pub mod renderable; pub mod ssh; mod sysinfo; diff --git a/mux/src/localpane.rs b/mux/src/localpane.rs index f44125a02..c8dd24390 100644 --- a/mux/src/localpane.rs +++ b/mux/src/localpane.rs @@ -1,5 +1,6 @@ use crate::domain::DomainId; use crate::pane::{CloseReason, Pane, PaneId, Pattern, SearchResult}; +use crate::procinfo::LocalProcessInfo; use crate::renderable::*; use crate::tmux::{TmuxDomain, TmuxDomainState}; use crate::{Domain, Mux, MuxNotification}; @@ -9,13 +10,11 @@ use config::keyassignment::ScrollbackEraseMode; use config::{configuration, ExitBehavior}; use portable_pty::{Child, ChildKiller, ExitStatus, MasterPty, PtySize}; use rangeset::RangeSet; -use serde::{Deserialize, Serialize}; use smol::channel::{bounded, Receiver, TryRecvError}; use std::cell::{RefCell, RefMut}; use std::collections::{HashMap, HashSet}; use std::io::Result as IoResult; use std::ops::Range; -use std::path::PathBuf; use std::sync::Arc; use termwiz::escape::DeviceControlMode; use termwiz::surface::{Line, SequenceNo, SEQ_ZERO}; @@ -337,7 +336,27 @@ impl Pane for LocalPane { None } - #[cfg(any(windows, target_os = "linux", target_os = "macos"))] + #[cfg(target_os = "macos")] + fn get_foreground_process_name(&self) -> Option { + let leader = self.pty.borrow().process_group_leader()?; + + let mut buffer: Vec = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _); + let x = unsafe { + libc::proc_pidpath( + leader, + buffer.as_mut_ptr() as *mut _, + libc::PROC_PIDPATHINFO_MAXSIZE as _, + ) + }; + if x > 0 { + unsafe { buffer.set_len(x as usize) }; + String::from_utf8(buffer).ok() + } else { + None + } + } + + #[cfg(any(windows, target_os = "linux"))] fn get_foreground_process_name(&self) -> Option { #[cfg(unix)] { @@ -836,111 +855,6 @@ impl LocalPane { } } -#[derive(Debug, Serialize, Deserialize, Copy, Clone)] -pub enum LocalProcessStatus { - Idle, - Run, - Sleep, - Stop, - Zombie, - Tracing, - Dead, - Wakekill, - Waking, - Parked, - LockBlocked, - Unknown, -} - -#[cfg(any(windows, target_os = "linux", target_os = "macos"))] -impl LocalProcessStatus { - fn from_process_status(status: sysinfo::ProcessStatus) -> Self { - match status { - sysinfo::ProcessStatus::Idle => Self::Idle, - sysinfo::ProcessStatus::Run => Self::Run, - sysinfo::ProcessStatus::Sleep => Self::Sleep, - sysinfo::ProcessStatus::Stop => Self::Stop, - sysinfo::ProcessStatus::Zombie => Self::Zombie, - sysinfo::ProcessStatus::Tracing => Self::Tracing, - sysinfo::ProcessStatus::Dead => Self::Dead, - sysinfo::ProcessStatus::Wakekill => Self::Wakekill, - sysinfo::ProcessStatus::Waking => Self::Waking, - sysinfo::ProcessStatus::Parked => Self::Parked, - sysinfo::ProcessStatus::LockBlocked => Self::LockBlocked, - sysinfo::ProcessStatus::Unknown(_) => Self::Unknown, - } - } -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct LocalProcessInfo { - pub pid: u32, - pub ppid: u32, - pub name: String, - pub executable: PathBuf, - pub argv: Vec, - pub cwd: PathBuf, - pub status: LocalProcessStatus, - pub children: HashMap, - pub start_time: u64, -} -luahelper::impl_lua_conversion!(LocalProcessInfo); - -impl LocalProcessInfo { - fn flatten_to_exe_names(&self) -> HashSet { - let mut names = HashSet::new(); - - fn flatten(item: &LocalProcessInfo, names: &mut HashSet) { - if let Some(exe) = item.executable.file_name() { - names.insert(exe.to_string_lossy().into_owned()); - } - for proc in item.children.values() { - flatten(proc, names); - } - } - - flatten(self, &mut names); - names - } -} - -#[cfg(any(windows, target_os = "linux", target_os = "macos"))] -impl LocalProcessInfo { - fn with_root_pid(system: &sysinfo::System, pid: u32) -> Option { - use sysinfo::{AsU32, Pid, Process, ProcessExt, SystemExt}; - - fn build_proc(proc: &Process, processes: &HashMap) -> LocalProcessInfo { - // Process has a `tasks` field but it does not correspond to child processes, - // so we need to repeatedly walk the full process list and establish that - // linkage for ourselves here - let mut children = HashMap::new(); - let pid = proc.pid(); - for (child_pid, child_proc) in processes { - if child_proc.parent() == Some(pid) { - children.insert(child_pid.as_u32(), build_proc(child_proc, processes)); - } - } - - LocalProcessInfo { - pid: proc.pid().as_u32(), - ppid: proc.parent().map(|pid| pid.as_u32()).unwrap_or(1), - name: proc.name().to_string(), - executable: proc.exe().to_path_buf(), - cwd: proc.cwd().to_path_buf(), - argv: proc.cmd().to_vec(), - start_time: proc.start_time(), - status: LocalProcessStatus::from_process_status(proc.status()), - children, - } - } - - let proc = system.process(pid as Pid)?; - let procs = system.processes(); - - Some(build_proc(&proc, &procs)) - } -} - impl Drop for LocalPane { fn drop(&mut self) { // Avoid lingering zombies if we can, but don't block forever. diff --git a/mux/src/procinfo.rs b/mux/src/procinfo.rs new file mode 100644 index 000000000..78d4e1d43 --- /dev/null +++ b/mux/src/procinfo.rs @@ -0,0 +1,108 @@ +use serde::{Deserialize, Serialize}; +use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; + +#[derive(Debug, Serialize, Deserialize, Copy, Clone)] +pub enum LocalProcessStatus { + Idle, + Run, + Sleep, + Stop, + Zombie, + Tracing, + Dead, + Wakekill, + Waking, + Parked, + LockBlocked, + Unknown, +} + +#[cfg(any(windows, target_os = "linux", target_os = "macos"))] +impl LocalProcessStatus { + fn from_process_status(status: sysinfo::ProcessStatus) -> Self { + match status { + sysinfo::ProcessStatus::Idle => Self::Idle, + sysinfo::ProcessStatus::Run => Self::Run, + sysinfo::ProcessStatus::Sleep => Self::Sleep, + sysinfo::ProcessStatus::Stop => Self::Stop, + sysinfo::ProcessStatus::Zombie => Self::Zombie, + sysinfo::ProcessStatus::Tracing => Self::Tracing, + sysinfo::ProcessStatus::Dead => Self::Dead, + sysinfo::ProcessStatus::Wakekill => Self::Wakekill, + sysinfo::ProcessStatus::Waking => Self::Waking, + sysinfo::ProcessStatus::Parked => Self::Parked, + sysinfo::ProcessStatus::LockBlocked => Self::LockBlocked, + sysinfo::ProcessStatus::Unknown(_) => Self::Unknown, + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct LocalProcessInfo { + pub pid: u32, + pub ppid: u32, + pub name: String, + pub executable: PathBuf, + pub argv: Vec, + pub cwd: PathBuf, + pub status: LocalProcessStatus, + pub children: HashMap, + pub start_time: u64, +} +luahelper::impl_lua_conversion!(LocalProcessInfo); + +impl LocalProcessInfo { + pub fn flatten_to_exe_names(&self) -> HashSet { + let mut names = HashSet::new(); + + fn flatten(item: &LocalProcessInfo, names: &mut HashSet) { + if let Some(exe) = item.executable.file_name() { + names.insert(exe.to_string_lossy().into_owned()); + } + for proc in item.children.values() { + flatten(proc, names); + } + } + + flatten(self, &mut names); + names + } +} + +#[cfg(any(windows, target_os = "linux", target_os = "macos"))] +impl LocalProcessInfo { + pub(crate) fn with_root_pid(system: &sysinfo::System, pid: u32) -> Option { + use sysinfo::{AsU32, Pid, Process, ProcessExt, SystemExt}; + + fn build_proc(proc: &Process, processes: &HashMap) -> LocalProcessInfo { + // Process has a `tasks` field but it does not correspond to child processes, + // so we need to repeatedly walk the full process list and establish that + // linkage for ourselves here + let mut children = HashMap::new(); + let pid = proc.pid(); + for (child_pid, child_proc) in processes { + if child_proc.parent() == Some(pid) { + children.insert(child_pid.as_u32(), build_proc(child_proc, processes)); + } + } + + LocalProcessInfo { + pid: proc.pid().as_u32(), + ppid: proc.parent().map(|pid| pid.as_u32()).unwrap_or(1), + name: proc.name().to_string(), + executable: proc.exe().to_path_buf(), + cwd: proc.cwd().to_path_buf(), + argv: proc.cmd().to_vec(), + start_time: proc.start_time(), + status: LocalProcessStatus::from_process_status(proc.status()), + children, + } + } + + let proc = system.process(pid as Pid)?; + let procs = system.processes(); + + Some(build_proc(&proc, &procs)) + } +}