From 39a2cc53dcab044fa0936ee5b0161a19104638ea Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Mon, 11 Dec 2023 00:44:48 -0800 Subject: [PATCH] Socket updates --- .../Sources/SecretAgentKit/Agent.swift | 8 +-- .../SecretAgentKit/SocketController.swift | 56 +++++++++++++------ .../OpenSSH/OpenSSHCertificateHandler.swift | 2 +- .../PublicKeyStandinFileController.swift | 2 +- Sources/SecretAgent/AppDelegate.swift | 3 +- .../Controllers/LaunchAgentController.swift | 10 ++-- 6 files changed, 53 insertions(+), 28 deletions(-) diff --git a/Sources/Packages/Sources/SecretAgentKit/Agent.swift b/Sources/Packages/Sources/SecretAgentKit/Agent.swift index 7588cf3..f4d5472 100644 --- a/Sources/Packages/Sources/SecretAgentKit/Agent.swift +++ b/Sources/Packages/Sources/SecretAgentKit/Agent.swift @@ -12,7 +12,7 @@ public class Agent { private let writer = OpenSSHKeyWriter() private let requestTracer = SigningRequestTracer() private let certificateHandler = OpenSSHCertificateHandler() - private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent.agent", category: "") + private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent") /// Initializes an agent with a store list and a witness. /// - Parameters: @@ -35,7 +35,7 @@ extension Agent { /// - writer: A ``FileHandleWriter`` to write the response to. /// - Return value: /// - Boolean if data could be read - @discardableResult public func handle(reader: FileHandleReader, writer: FileHandleWriter) -> Bool { + @discardableResult public func handle(reader: FileHandleReader, writer: FileHandleWriter) async -> Bool { logger.debug("Agent handling new data") let data = Data(reader.availableData) guard data.count > 4 else { return false} @@ -47,12 +47,12 @@ extension Agent { } logger.debug("Agent handling request of type \(requestType.debugDescription)") let subData = Data(data[5...]) - let response = handle(requestType: requestType, data: subData, reader: reader) + let response = await handle(requestType: requestType, data: subData, reader: reader) writer.write(response) return true } - func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data { + func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) async -> Data { // Depending on the launch context (such as after macOS update), the agent may need to reload secrets before acting reloadSecretsIfNeccessary() var response = Data() diff --git a/Sources/Packages/Sources/SecretAgentKit/SocketController.swift b/Sources/Packages/Sources/SecretAgentKit/SocketController.swift index 3143d99..43243db 100644 --- a/Sources/Packages/Sources/SecretAgentKit/SocketController.swift +++ b/Sources/Packages/Sources/SecretAgentKit/SocketController.swift @@ -10,22 +10,24 @@ public class SocketController { private var port: SocketPort? /// A handler that will be notified when a new read/write handle is available. /// False if no data could be read - public var handler: ((FileHandleReader, FileHandleWriter) -> Bool)? + public var handler: ((FileHandleReader, FileHandleWriter) async -> Bool)? + /// Logger. + private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "SocketController") /// Initializes a socket controller with a specified path. /// - Parameter path: The path to use as a socket. public init(path: String) { - Logger().debug("Socket controller setting up at \(path)") + logger.debug("Socket controller setting up at \(path)") if let _ = try? FileManager.default.removeItem(atPath: path) { - Logger().debug("Socket controller removed existing socket") + logger.debug("Socket controller removed existing socket") } let exists = FileManager.default.fileExists(atPath: path) assert(!exists) - Logger().debug("Socket controller path is clear") + logger.debug("Socket controller path is clear") port = socketPort(at: path) configureSocket(at: path) - Logger().debug("Socket listening at \(path)") + logger.debug("Socket listening at \(path)") } /// Configures the socket and a corresponding FileHandle. @@ -35,7 +37,7 @@ public class SocketController { fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true) NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionAccept(notification:)), name: .NSFileHandleConnectionAccepted, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionDataAvailable(notification:)), name: .NSFileHandleDataAvailable, object: nil) - fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!]) + fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.Mode.common]) } /// Creates a SocketPort for a path. @@ -65,25 +67,45 @@ public class SocketController { /// Handles a new connection being accepted, invokes the handler, and prepares to accept new connections. /// - Parameter notification: A `Notification` that triggered the call. @objc func handleConnectionAccept(notification: Notification) { - Logger().debug("Socket controller accepted connection") + logger.debug("Socket controller accepted connection") guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return } - _ = handler?(new, new) - new.waitForDataInBackgroundAndNotify() - fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!]) + Task { + _ = await handler?(new, new) + await new.waitForDataInBackgroundAndNotifyOnMainActor() + await fileHandle?.acceptConnectionInBackgroundAndNotifyOnMainActor() + } } /// Handles a new connection providing data and invokes the handler callback. /// - Parameter notification: A `Notification` that triggered the call. @objc func handleConnectionDataAvailable(notification: Notification) { - Logger().debug("Socket controller has new data available") + logger.debug("Socket controller has new data available") guard let new = notification.object as? FileHandle else { return } - Logger().debug("Socket controller received new file handle") - if((handler?(new, new)) == true) { - Logger().debug("Socket controller handled data, wait for more data") - new.waitForDataInBackgroundAndNotify() - } else { - Logger().debug("Socket controller called with empty data, socked closed") + logger.debug("Socket controller received new file handle") + Task { + if((await handler?(new, new)) == true) { + logger.debug("Socket controller handled data, wait for more data") + await new.waitForDataInBackgroundAndNotifyOnMainActor() + } else { + logger.debug("Socket controller called with empty data, socked closed") + } } } } + +extension FileHandle { + + /// Ensures waitForDataInBackgroundAndNotify will be called on the main actor. + @MainActor func waitForDataInBackgroundAndNotifyOnMainActor() { + waitForDataInBackgroundAndNotify() + } + + + /// Ensures acceptConnectionInBackgroundAndNotify will be called on the main actor. + /// - Parameter modes: the runloop modes to use. + @MainActor func acceptConnectionInBackgroundAndNotifyOnMainActor(forModes modes: [RunLoop.Mode]? = [RunLoop.Mode.common]) { + acceptConnectionInBackgroundAndNotify(forModes: modes) + } + +} diff --git a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift b/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift index 435c310..12ecefa 100644 --- a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift +++ b/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift @@ -5,7 +5,7 @@ import OSLog public class OpenSSHCertificateHandler { private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory()) - private let logger = Logger() + private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "OpenSSHCertificateHandler") private let writer = OpenSSHKeyWriter() private var keyBlobsAndNames: [AnySecret: (Data, Data)] = [:] diff --git a/Sources/Packages/Sources/SecretKit/PublicKeyStandinFileController.swift b/Sources/Packages/Sources/SecretKit/PublicKeyStandinFileController.swift index 87fe352..7ec0a62 100644 --- a/Sources/Packages/Sources/SecretKit/PublicKeyStandinFileController.swift +++ b/Sources/Packages/Sources/SecretKit/PublicKeyStandinFileController.swift @@ -4,7 +4,7 @@ import OSLog /// Controller responsible for writing public keys to disk, so that they're easily accessible by scripts. public class PublicKeyFileStoreController { - private let logger = Logger() + private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "PublicKeyFileStoreController") private let directory: String private let keyWriter = OpenSSHKeyWriter() diff --git a/Sources/SecretAgent/AppDelegate.swift b/Sources/SecretAgent/AppDelegate.swift index e21587f..ab6a3cd 100644 --- a/Sources/SecretAgent/AppDelegate.swift +++ b/Sources/SecretAgent/AppDelegate.swift @@ -27,9 +27,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { return SocketController(path: path) }() private var updateSink: AnyCancellable? + private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate") func applicationDidFinishLaunching(_ aNotification: Notification) { - Logger().debug("SecretAgent finished launching") + logger.debug("SecretAgent finished launching") DispatchQueue.main.async { self.socketController.handler = self.agent.handle(reader:writer:) } diff --git a/Sources/Secretive/Controllers/LaunchAgentController.swift b/Sources/Secretive/Controllers/LaunchAgentController.swift index d50299d..7f512aa 100644 --- a/Sources/Secretive/Controllers/LaunchAgentController.swift +++ b/Sources/Secretive/Controllers/LaunchAgentController.swift @@ -6,8 +6,10 @@ import SecretKit struct LaunchAgentController { + private let logger = Logger(subsystem: "com.maxgoedjen.secretive", category: "LaunchAgentController") + func install(completion: (() -> Void)? = nil) { - Logger().debug("Installing agent") + logger.debug("Installing agent") _ = setEnabled(false) // This is definitely a bit of a "seems to work better" thing but: // Seems to more reliably hit if these are on separate runloops, otherwise it seems like it sometimes doesn't kill old @@ -20,7 +22,7 @@ struct LaunchAgentController { } func forceLaunch(completion: ((Bool) -> Void)?) { - Logger().debug("Agent is not running, attempting to force launch") + logger.debug("Agent is not running, attempting to force launch") let url = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LoginItems/SecretAgent.app") let config = NSWorkspace.OpenConfiguration() config.activates = false @@ -29,9 +31,9 @@ struct LaunchAgentController { completion?(error == nil) } if let error = error { - Logger().error("Error force launching \(error.localizedDescription)") + logger.error("Error force launching \(error.localizedDescription)") } else { - Logger().debug("Agent force launched") + logger.debug("Agent force launched") } } }