Last active
May 2, 2025 18:45
-
-
Save JJTech0130/b58f2bc19ca394c8b8957dc318d7ea57 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import Darwin | |
import Logging | |
// MARK: - Dynamic Symbol Lookup | |
func resolve<T>(_ base64Symbol: String) -> T? { | |
let decoded = Data(base64Encoded: base64Symbol)! | |
let symbol = decoded.withUnsafeBytes { ptr in | |
String(cString: ptr.bindMemory(to: CChar.self).baseAddress!) | |
} | |
guard let handle = dlopen(nil, RTLD_NOW), | |
let sym = dlsym(handle, symbol) else { | |
return nil | |
} | |
return unsafeBitCast(sym, to: T.self) | |
} | |
let _proc_pidinfo: (@convention(c) (Int32, Int32, UInt64, UnsafeMutableRawPointer?, Int32) -> Int32)? = | |
resolve("cHJvY19waWRpbmZv") | |
let _proc_pidfdinfo: (@convention(c) (Int32, Int32, Int32, UnsafeMutableRawPointer?, Int32) -> Int32)? = | |
resolve("cHJvY19waWRmZGluZm8=") | |
let _sqlite3_lockstate: (@convention(c) (UnsafePointer<CChar>, Int32) -> Int32)? = | |
resolve("X3NxbGl0ZTNfbG9ja3N0YXRl") | |
// MARK: - Filtering | |
let ignoredPrefixes = ["/System", "/usr/lib", "/dev"] | |
let ignoredSuffixes = ["-wal", "-shm", "-journal"] | |
func isIgnoredPath(_ path: String) -> Bool { | |
ignoredPrefixes.contains { path.hasPrefix($0) } | |
} | |
func hasIgnoredSuffix(_ path: String) -> Bool { | |
ignoredSuffixes.contains { path.hasSuffix($0) } | |
} | |
func hasRunningBoardXattr(_ path: String) -> Bool { | |
getxattr(path, "com.apple.runningboard.can-suspend-locked", nil, 0, 0, 0) >= 0 | |
} | |
// MARK: - SQLite Lockstate Hack | |
func SQLiteDatabaseState(_ path: String, by pid: Int32 = getpid()) -> Int { | |
guard let sqlite3_lockstate = _sqlite3_lockstate else { return -1 } | |
return path.withCString { cPath in | |
Int(sqlite3_lockstate(cPath, pid)) | |
} | |
} | |
// MARK: - Lock Checker | |
public extension Logging.Logger { | |
func checkLock(fd: Int32) { | |
guard let proc_pidfdinfo = _proc_pidfdinfo else { | |
self.error("Missing required dlsym symbols.") | |
return | |
} | |
var vnodeInfo = new_vnode_fdinfowithpath() | |
let vnodeInfoSize = Int32(MemoryLayout.size(ofValue: vnodeInfo)) | |
let result = proc_pidfdinfo(getpid(), fd, Int32(PROC_PIDFDVNODEPATHINFO), &vnodeInfo, vnodeInfoSize) | |
guard result == vnodeInfoSize else { return } | |
let rawPath = withUnsafePointer(to: &vnodeInfo.pvip.vip_path) { | |
$0.withMemoryRebound(to: CChar.self, capacity: 1024) { | |
String(cString: $0) | |
} | |
} | |
guard let normalizedPath = URL(fileURLWithPath: rawPath).standardized.path.removingPercentEncoding else { | |
return | |
} | |
if isIgnoredPath(normalizedPath) || | |
hasIgnoredSuffix(normalizedPath) || | |
hasRunningBoardXattr(normalizedPath) { | |
return | |
} | |
let dbState = SQLiteDatabaseState(normalizedPath) | |
if dbState == 1 { | |
self.warning("π SQLite database locked: \(normalizedPath)") | |
return | |
} else if dbState == 0 { | |
self.info("β SQLite database unlocked: \(normalizedPath)") | |
return | |
} | |
let reopenFD = open(normalizedPath, O_RDONLY) | |
guard reopenFD >= 0 else { | |
self.error("Could not reopen \(normalizedPath)") | |
return | |
} | |
defer { close(reopenFD) } | |
var lock = flock() | |
lock.l_type = Int16(F_WRLCK) | |
lock.l_whence = Int16(SEEK_SET) | |
lock.l_start = 0 | |
lock.l_len = 0 | |
if fcntl(reopenFD, F_GETLK, &lock) == -1 { | |
self.error("fcntl failed on \(normalizedPath)") | |
return | |
} | |
if lock.l_type != F_UNLCK { | |
let type = (lock.l_type == F_RDLCK) ? "read lock" : "write lock" | |
self.warning("π File locked: \(normalizedPath) (\(type)) by PID \(lock.l_pid)") | |
} else { | |
self.info("β File unlocked: \(normalizedPath)") | |
} | |
} | |
func debugLocks() { | |
guard let proc_pidinfo = _proc_pidinfo else { | |
self.error("Missing required dlsym symbols.") | |
return | |
} | |
let pid = getpid() | |
let bufferSize = Int(proc_pidinfo(pid, Int32(PROC_PIDLISTFDS), 0, nil, 0)) | |
guard bufferSize > 0 else { | |
self.error("Failed to get FD list size") | |
return | |
} | |
let fdCount = bufferSize / MemoryLayout<proc_fdinfo>.stride | |
let fdInfos = UnsafeMutablePointer<proc_fdinfo>.allocate(capacity: fdCount) | |
defer { fdInfos.deallocate() } | |
let actualSize = proc_pidinfo(pid, Int32(PROC_PIDLISTFDS), 0, fdInfos, Int32(bufferSize)) | |
guard actualSize > 0 else { | |
self.error("Failed to get FD info") | |
return | |
} | |
for i in 0..<fdCount { | |
let fdInfo = fdInfos[i] | |
if fdInfo.proc_fdtype == 1 /* PROX_FDTYPE_VNODE */ { | |
checkLock(fd: fdInfo.proc_fd) | |
} | |
} | |
} | |
} | |
// MARK: - Manual Struct Definitions | |
let PROC_PIDLISTFDS = 1 | |
let PROC_PIDFDVNODEPATHINFO = 2 | |
struct proc_fdinfo { | |
var proc_fd: Int32 | |
var proc_fdtype: UInt32 | |
} | |
struct vnode_info { | |
var vi_stat: ( | |
UInt32, UInt16, UInt16, UInt64, uid_t, gid_t, | |
Int64, Int64, Int64, Int64, Int64, Int64, | |
Int64, Int64, off_t, Int64, Int32, | |
UInt32, UInt32, UInt32, (Int64, Int64) | |
) | |
var vi_type: Int32 | |
var vi_pad: Int32 | |
var vi_fsid: (Int32, Int32) // fsid_t | |
} | |
func new_vnode_info() -> vnode_info { | |
return vnode_info(vi_stat: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (0, 0)), vi_type: 0, vi_pad: 0, vi_fsid: (0, 0)) | |
} | |
struct vnode_info_path { | |
var vip_vi: vnode_info | |
var vip_path: MAX_PATH | |
} | |
struct proc_fileinfo { | |
var fi_openflags: UInt32 | |
var fi_status: UInt32 | |
var fi_offset: off_t | |
var fi_type: Int32 | |
var fi_guardflags: UInt32 | |
} | |
func new_proc_fileinfo() -> proc_fileinfo { | |
return proc_fileinfo(fi_openflags: 0, fi_status: 0, fi_offset: 0, fi_type: 0, fi_guardflags: 0) | |
} | |
struct vnode_fdinfowithpath { | |
var pfi: proc_fileinfo | |
var pvip: vnode_info_path | |
} | |
func new_vnode_fdinfowithpath() -> vnode_fdinfowithpath { | |
return vnode_fdinfowithpath(pfi: new_proc_fileinfo(), pvip: vnode_info_path(vip_vi: new_vnode_info(), vip_path: new_max_path())) | |
} | |
// MARK: - fixed-size array nonsense | |
// seriously, who came up with this? | |
// swiftlint:disable comma | |
typealias MAX_PATH = (CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar,CChar) | |
func new_max_path() -> MAX_PATH { | |
return (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) | |
} | |
// swiftlint:enable comma |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment