diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-12 13:58:32 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-12 13:59:32 +0300 |
| commit | bcea1d884a959a9865a9a85aad4589f5b0853d2b (patch) | |
| tree | 48908f0e7b3f329c850aa986117cadf8002b37f3 /src | |
| parent | ea3f09dceae123aa0dbe5a54cf82c8d8bb5df8a6 (diff) | |
fix: detect macos foreground jobs by process group
fixes #100
Diffstat (limited to 'src')
| -rw-r--r-- | src/platform/macos.rs | 99 |
1 files changed, 69 insertions, 30 deletions
diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 43c24386..2e9b83b5 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -6,6 +6,8 @@ use std::process::{Command, Stdio}; use super::{ClipboardCommand, ForegroundJob, ForegroundProcess, Signal}; +const PROC_PGRP_ONLY: u32 = 2; + /// Collect the foreground terminal job for a given child PID. pub fn foreground_job(child_pid: u32) -> Option<ForegroundJob> { if child_pid == 0 { @@ -13,25 +15,9 @@ pub fn foreground_job(child_pid: u32) -> Option<ForegroundJob> { } let fg_pgid = foreground_process_group_id(child_pid)?; - let mut pids = vec![0i32; 4096]; - let bytes = unsafe { - libc::proc_listallpids( - pids.as_mut_ptr() as *mut libc::c_void, - (pids.len() * std::mem::size_of::<i32>()) as libc::c_int, - ) - }; - if bytes <= 0 { - return None; - } - - let count = (bytes as usize) / std::mem::size_of::<i32>(); let mut processes = Vec::new(); - for raw_pid in pids.into_iter().take(count) { - if raw_pid <= 0 { - continue; - } - let pid = raw_pid as u32; + for pid in process_group_pids(fg_pgid) { let Some(info) = process_bsdinfo(pid) else { continue; }; @@ -60,6 +46,35 @@ pub fn foreground_job(child_pid: u32) -> Option<ForegroundJob> { }) } +fn process_group_pids(process_group_id: u32) -> Vec<u32> { + let mut capacity = 16usize; + + for _ in 0..8 { + let mut pids = vec![0 as libc::pid_t; capacity]; + let buffer_bytes = pids.len() * std::mem::size_of::<libc::pid_t>(); + let returned_bytes = unsafe { + libc::proc_listpids( + PROC_PGRP_ONLY, + process_group_id, + pids.as_mut_ptr() as *mut libc::c_void, + buffer_bytes as libc::c_int, + ) + }; + if returned_bytes <= 0 { + return Vec::new(); + } + + let returned_bytes = returned_bytes as usize; + let count = returned_bytes / std::mem::size_of::<libc::pid_t>(); + if returned_bytes < buffer_bytes { + return collect_positive_pids(pids, count); + } + capacity = capacity.saturating_mul(2); + } + + Vec::new() +} + /// Read `e_tpgid` (foreground process group of the controlling terminal) /// for the given PID. pub fn foreground_process_group_id(pid: u32) -> Option<u32> { @@ -81,10 +96,10 @@ pub fn foreground_process_group_id(pid: u32) -> Option<u32> { } let fg = info.e_tpgid; - if fg == 0 { - None + if fg > 0 { + Some(fg as u32) } else { - Some(fg) + None } } @@ -352,22 +367,46 @@ pub fn session_processes(child_pid: u32) -> Vec<u32> { return Vec::new(); } - let mut pids = vec![0i32; 4096]; - let bytes = unsafe { - libc::proc_listallpids( - pids.as_mut_ptr() as *mut libc::c_void, - (pids.len() * std::mem::size_of::<i32>()) as libc::c_int, - ) + all_pids() + .into_iter() + .filter(|pid| unsafe { libc::getsid(*pid as libc::pid_t) } == target_session) + .collect() +} + +fn all_pids() -> Vec<u32> { + let initial_count = unsafe { libc::proc_listallpids(std::ptr::null_mut(), 0) }; + let mut capacity = if initial_count > 0 { + initial_count as usize + 128 + } else { + 4096 }; - if bytes <= 0 { - return Vec::new(); + + for _ in 0..8 { + let mut pids = vec![0 as libc::pid_t; capacity]; + let count = unsafe { + libc::proc_listallpids( + pids.as_mut_ptr() as *mut libc::c_void, + (pids.len() * std::mem::size_of::<libc::pid_t>()) as libc::c_int, + ) + }; + if count <= 0 { + return Vec::new(); + } + + let count = count as usize; + if count < capacity { + return collect_positive_pids(pids, count); + } + capacity = capacity.saturating_mul(2); } - let count = (bytes as usize) / std::mem::size_of::<i32>(); + Vec::new() +} + +fn collect_positive_pids(pids: Vec<libc::pid_t>, count: usize) -> Vec<u32> { pids.into_iter() .take(count) .filter(|pid| *pid > 0) - .filter(|pid| unsafe { libc::getsid(*pid) } == target_session) .map(|pid| pid as u32) .collect() } |
