import Darwin import Foundation import OSLog /// Handles process tree traversal and process information extraction. @MainActor final class ProcessTracker { private let logger = Logger( subsystem: BundleIdentifiers.loggerSubsystem, category: "ProcessTracker" ) /// Get the parent process ID of a given process func getParentProcessID(of pid: pid_t) -> pid_t? { var info = kinfo_proc() var size = MemoryLayout.size var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid] let result = sysctl(&mib, u_int(mib.count), &info, &size, nil, 0) if result == 0 && size > 0 { return info.kp_eproc.e_ppid } return nil } /// Get process info including name func getProcessInfo(for pid: pid_t) -> (name: String, ppid: pid_t)? { var info = kinfo_proc() var size = MemoryLayout.size var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid] let result = sysctl(&mib, u_int(mib.count), &info, &size, nil, 0) if result == 0 && size > 0 { let name = withUnsafeBytes(of: &info.kp_proc.p_comm) { bytes in let commBytes = bytes.bindMemory(to: CChar.self) guard let baseAddress = commBytes.baseAddress else { return "" } return String(cString: baseAddress) } return (name: name, ppid: info.kp_eproc.e_ppid) } return nil } /// Log the process tree for debugging func logProcessTree(for pid: pid_t) { logger.debug("Process tree for PID \(pid):") var currentPID = pid var depth = 0 while depth < 20 { if let info = getProcessInfo(for: currentPID) { let indent = String(repeating: " ", count: depth) logger.debug("\(indent)PID \(currentPID): \(info.name)") if info.ppid == 0 || info.ppid == 1 { break } currentPID = info.ppid depth += 1 } else { break } } } /// Find the terminal process in the ancestry of a given PID func findTerminalAncestor(for pid: pid_t, maxDepth: Int = 10) -> pid_t? { var currentPID = pid var depth = 0 while depth < maxDepth { if let parentPID = getParentProcessID(of: currentPID) { logger.debug("Checking ancestor process PID: \(parentPID) at depth \(depth + 1)") // Check if this is a terminal process by examining windows // This will be coordinated with WindowEnumerator currentPID = parentPID depth += 1 } else { break } } return nil } }