mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-08 22:33:26 +01:00
- [ConsoleLogger]: Fix race conditions during shutdown and possible crashes by writing to closed stream handler
This commit is contained in:
@@ -59,48 +59,64 @@ public class AbstractConsoleLogger<T: OutputStream>: ConsoleLogger{
|
|||||||
originalStdout = dup(STDOUT_FILENO)
|
originalStdout = dup(STDOUT_FILENO)
|
||||||
originalStderr = dup(STDERR_FILENO)
|
originalStderr = dup(STDERR_FILENO)
|
||||||
|
|
||||||
|
let redirectedOutStream = self.outPipe?.fileHandleForWriting.fileDescriptor ?? -1
|
||||||
|
let redirectedErrStream = self.errPipe?.fileHandleForWriting.fileDescriptor ?? -1
|
||||||
|
|
||||||
// Redirect stdout and stderr to our pipes
|
// Redirect stdout and stderr to our pipes
|
||||||
dup2(self.outPipe?.fileHandleForWriting.fileDescriptor ?? -1, STDOUT_FILENO)
|
dup2(redirectedOutStream, STDOUT_FILENO)
|
||||||
dup2(self.errPipe?.fileHandleForWriting.fileDescriptor ?? -1, STDERR_FILENO)
|
dup2(redirectedErrStream, STDERR_FILENO)
|
||||||
|
|
||||||
|
// Disable libc-level buffering
|
||||||
|
// (libc by default uses bufferring except its own console/TTYs such as for pipes)
|
||||||
|
// we do have our own buffering so we disable stdlib io level bufferring
|
||||||
|
setvbuf(stdout, nil, _IONBF, 0) // disable buffering for stdout
|
||||||
|
setvbuf(stderr, nil, _IONBF, 0) // disable buffering for stderr
|
||||||
|
|
||||||
// Setup readability handlers for raw data
|
// Setup readability handlers for raw data
|
||||||
setupReadabilityHandler(for: outputHandle, isError: false)
|
setupReadabilityHandler(for: outputHandle, isError: false)
|
||||||
setupReadabilityHandler(for: errorHandle, isError: true)
|
setupReadabilityHandler(for: errorHandle, isError: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let shutdownLock = NSLock()
|
||||||
|
|
||||||
private func setupReadabilityHandler(for handle: FileHandle?, isError: Bool) {
|
private func setupReadabilityHandler(for handle: FileHandle?, isError: Bool) {
|
||||||
handle?.readabilityHandler = { [weak self] handle in
|
handle?.readabilityHandler = readHandler(for: handle, isError: isError)
|
||||||
let data = handle.availableData
|
}
|
||||||
if !data.isEmpty {
|
|
||||||
self?.writeQueue.async {
|
private func readHandler(for handle: FileHandle?, isError: Bool) -> (FileHandle) -> Void {
|
||||||
try? self?.writeData(data)
|
return { [weak self] _ in
|
||||||
}
|
guard let self, let data = handle?.availableData else { return }
|
||||||
|
|
||||||
// Forward to original std stream
|
shutdownLock.lock()
|
||||||
if let originalFD = isError ? self?.originalStderr : self?.originalStdout {
|
defer { shutdownLock.unlock() }
|
||||||
data.withUnsafeBytes { (bufferPointer) -> Void in
|
|
||||||
if let baseAddress = bufferPointer.baseAddress, bufferPointer.count > 0 {
|
writeQueue.async {
|
||||||
write(originalFD, baseAddress, bufferPointer.count)
|
try? self.writeData(data)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
if let fd = isError ? self.originalStderr : self.originalStdout {
|
||||||
|
data.withUnsafeBytes { _ = write(fd, $0.baseAddress, $0.count) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func writeData(_ data: Data) throws {
|
func writeData(_ data: Data) throws {
|
||||||
throw AbstractClassError.abstractMethodInvoked
|
throw AbstractClassError.abstractMethodInvoked
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopCapturing() {
|
func stopCapturing() {
|
||||||
|
shutdownLock.lock()
|
||||||
|
defer { shutdownLock.unlock() }
|
||||||
|
|
||||||
ostream.close()
|
ostream.close()
|
||||||
|
|
||||||
// Restore original stdout and stderr
|
// Restore original stdout and stderr
|
||||||
if let stdout = originalStdout {
|
if let stdout = originalStdout, stdout != STDOUT_FILENO {
|
||||||
dup2(stdout, STDOUT_FILENO)
|
dup2(stdout, STDOUT_FILENO)
|
||||||
close(stdout)
|
close(stdout)
|
||||||
}
|
}
|
||||||
if let stderr = originalStderr {
|
if let stderr = originalStderr, stderr != STDERR_FILENO {
|
||||||
dup2(stderr, STDERR_FILENO)
|
dup2(stderr, STDERR_FILENO)
|
||||||
close(stderr)
|
close(stderr)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user