vibetunnel/mac/VibeTunnel/Core/Services/WindowTracking/ProcessTracker.swift

94 lines
2.8 KiB
Swift

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<kinfo_proc>.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<kinfo_proc>.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
}
}