1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 05:12:40 +03:00

procinfo: reorg a bit; enable cwd probing on windows

Likely breaks the mac and windows builds
This commit is contained in:
Wez Furlong 2021-12-31 21:28:03 -07:00
parent e16a27dfb3
commit 1ad4015f9c
5 changed files with 113 additions and 104 deletions

View File

@ -331,77 +331,20 @@ impl Pane for LocalPane {
.or_else(|| self.divine_current_working_dir())
}
#[cfg(not(any(windows, target_os = "linux", target_os = "macos")))]
fn get_foreground_process_name(&self) -> Option<String> {
None
}
#[cfg(target_os = "macos")]
fn get_foreground_process_name(&self) -> Option<String> {
let leader = self.pty.borrow().process_group_leader()?;
let mut buffer: Vec<u8> = 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(target_os = "linux")]
fn get_foreground_process_name(&self) -> Option<String> {
#[cfg(unix)]
if let Some(pid) = self.pty.borrow().process_group_leader() {
if let Ok(path) = std::fs::read_link(format!("/proc/{}/exe", pid)) {
if let Ok(path) = LocalProcessInfo::executable_path(pid) {
return Some(path.to_string_lossy().to_string());
}
}
None
}
#[cfg(windows)]
fn get_foreground_process_name(&self) -> Option<String> {
// Windows doesn't have any job control or session concept,
// so we infer that the equivalent to the process group
// leader is the most recently spawned program running
// in the console
if let Some(root_proc) = self.divine_process_list(false) {
let mut youngest = &root_proc;
fn find_youngest<'a>(proc: &'a LocalProcessInfo, youngest: &mut &'a LocalProcessInfo) {
if proc.start_time >= youngest.start_time {
// start_time has only 1 second granularity, and spawning
// a child process will typically spawn a console host at
// the same time.
// We might traverse one snapshot of the tree differently
// from another due to random seeds in the hash table,
// so we do a little bit of targeted workaround here
// to suppress the console hosts from candidate child
// processes.
let ignore = proc.name == "OpenConsole.exe" || proc.name == "conhost.exe";
if !ignore {
*youngest = proc;
}
}
for child in proc.children.values() {
find_youngest(child, youngest);
}
}
find_youngest(&root_proc, &mut youngest);
Some(youngest.executable.to_string_lossy().to_string())
} else {
None
#[cfg(windows)]
if let Some(fg) = self.divine_foreground_process() {
return Some(fg.executable.to_string_lossy().to_string());
}
None
}
fn can_close_without_prompting(&self, _reason: CloseReason) -> bool {
@ -783,50 +726,17 @@ impl LocalPane {
}
}
#[cfg(target_os = "macos")]
fn divine_current_working_dir_macos(&self) -> Option<Url> {
fn divine_current_working_dir(&self) -> Option<Url> {
#[cfg(unix)]
if let Some(pid) = self.pty.borrow().process_group_leader() {
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 _) };
if let Ok(s) = path.to_str() {
return Url::parse(&format!("file://localhost{}", s)).ok();
}
}
}
None
}
#[cfg(target_os = "linux")]
fn divine_current_working_dir_linux(&self) -> Option<Url> {
if let Some(pid) = self.pty.borrow().process_group_leader() {
if let Ok(path) = std::fs::read_link(format!("/proc/{}/cwd", pid)) {
if let Some(path) = LocalProcessInfo::current_working_dir(pid) {
return Url::parse(&format!("file://localhost{}", path.display())).ok();
}
}
None
}
fn divine_current_working_dir(&self) -> Option<Url> {
#[cfg(target_os = "linux")]
{
return self.divine_current_working_dir_linux();
}
#[cfg(target_os = "macos")]
{
return self.divine_current_working_dir_macos();
#[cfg(windows)]
if let Some(fg) = self.divine_foreground_process() {
return Url::parse(&format!("file://localhost{}", fg.cwd.display())).ok();
}
#[allow(unreachable_code)]
@ -839,6 +749,43 @@ impl LocalPane {
}
None
}
fn divine_foreground_process(&self) -> Option<LocalProcessInfo> {
// Windows doesn't have any job control or session concept,
// so we infer that the equivalent to the process group
// leader is the most recently spawned program running
// in the console
if let Some(root_proc) = self.divine_process_list(false) {
let mut youngest = &root_proc;
fn find_youngest<'a>(proc: &'a LocalProcessInfo, youngest: &mut &'a LocalProcessInfo) {
if proc.start_time >= youngest.start_time {
// start_time has only 1 second granularity, and spawning
// a child process will typically spawn a console host at
// the same time.
// We might traverse one snapshot of the tree differently
// from another due to random seeds in the hash table,
// so we do a little bit of targeted workaround here
// to suppress the console hosts from candidate child
// processes.
let ignore = proc.name == "OpenConsole.exe" || proc.name == "conhost.exe";
if !ignore {
*youngest = proc;
}
}
for child in proc.children.values() {
find_youngest(child, youngest);
}
}
find_youngest(&root_proc, &mut youngest);
Some(youngest.clone())
} else {
None
}
}
}
impl Drop for LocalPane {

View File

@ -57,4 +57,9 @@ impl LocalProcessInfo {
pub fn with_root_pid(_pid: u32) -> Option<Self> {
None
}
#[cfg(not(any(target_os = "macos", target_os = "linux", windows)))]
pub fn current_working_dir(_pid: u32) -> Option<PathBuf> {
None
}
}

View File

@ -20,6 +20,14 @@ impl From<&str> for LocalProcessStatus {
}
impl LocalProcessInfo {
pub fn current_working_dir(pid: u32) -> Option<PathBuf> {
std::fs::read_link(format!("/proc/{}/cwd", pid)).ok()
}
fn executable_path(pid: u32) -> Option<PathBuf> {
std::fs::read_link(format!("/proc/{}/exe", pid)).ok()
}
pub fn with_root_pid(pid: u32) -> Option<Self> {
use libc::pid_t;
@ -73,7 +81,7 @@ impl LocalProcessInfo {
std::fs::read_link(format!("/proc/{}/exe", pid)).unwrap_or_else(|_| PathBuf::new())
}
fn cwd_for_pid(pid: pid_t) -> PathBuf {
std::fs::read_link(format!("/proc/{}/cwd", pid)).unwrap_or_else(|_| PathBuf::new())
current_working_dir(pid).unwrap_or_else(|| PathBuf::new())
}
fn parse_cmdline(pid: pid_t) -> Vec<String> {

View File

@ -15,6 +15,44 @@ impl From<u32> for LocalProcessStatus {
}
impl LocalProcessInfo {
pub fn current_working_dir(pid: libc::pid_t) -> Option<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 {
return None;
}
let nul = pathinfo.pvi_cdir.vip_path.iter().position(|&c| c == 0)?;
Some(OsStr::from_bytes(&pathinfo.pvi_cdir.vip_path[0..nul]).into())
}
fn executable_path(pid: u32) -> Option<PathBuf> {
let mut buffer: Vec<u8> = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _);
let x = unsafe {
libc::proc_pidpath(
pid as _,
buffer.as_mut_ptr() as *mut _,
libc::PROC_PIDPATHINFO_MAXSIZE as _,
)
};
if x <= 0 {
return None;
}
unsafe { buffer.set_len(x as usize) };
Some(OsString::from_vec(buffer).into())
}
pub fn with_root_pid(pid: u32) -> Option<Self> {
/// Enumerate all current process identifiers
fn all_pids() -> Vec<libc::pid_t> {

View File

@ -315,6 +315,17 @@ impl Drop for ProcHandle {
}
impl LocalProcessInfo {
pub fn current_working_dir(pid: u32) -> Option<PathBuf> {
let proc = ProcHandle::new(pid)?;
let params = proc.get_params()?;
Some(params.cwd)
}
pub fn executable_path(pid: u32) -> Option<PathBuf> {
let proc = ProcHandle::new(pid)?;
proc.executable()
}
pub fn with_root_pid(pid: u32) -> Option<Self> {
let snapshot = Snapshot::new()?;
let procs: Vec<_> = snapshot.iter().collect();