Skip to content

Instantly share code, notes, and snippets.

@JJTech0130
Last active May 2, 2025 18:45
Show Gist options
  • Save JJTech0130/b58f2bc19ca394c8b8957dc318d7ea57 to your computer and use it in GitHub Desktop.
Save JJTech0130/b58f2bc19ca394c8b8957dc318d7ea57 to your computer and use it in GitHub Desktop.
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