From df4661a7182a26514b02fd38a31c7b852ac9d767 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sun, 2 Jan 2022 22:52:53 -0800 Subject: [PATCH] . --- Sources/SecretAgent/AppDelegate.swift | 31 +++++++++++-------- Sources/Secretive/App.swift | 11 ++++--- .../AgentCommunicationController.swift | 25 ++++++++------- .../Controllers/AgentLaunchController.swift | 14 +++++++-- .../Controllers/AgentStatusChecker.swift | 7 ++++- .../Secretive/Views/CreateSecretView.swift | 2 +- 6 files changed, 57 insertions(+), 33 deletions(-) diff --git a/Sources/SecretAgent/AppDelegate.swift b/Sources/SecretAgent/AppDelegate.swift index c2a155f..f935480 100644 --- a/Sources/SecretAgent/AppDelegate.swift +++ b/Sources/SecretAgent/AppDelegate.swift @@ -45,6 +45,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol { guard let update = update else { return } self.notifier.notify(update: update, ignore: self.updater.ignore(release:)) } + // TODO: REMOVE + notifier.notify(update: Release(name: "Test", prerelease: false, html_url: URL(string: "https://example.com")!, body: ""), ignore: nil) connect() } @@ -55,6 +57,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol { } func updatedStore(withID id: UUID) async throws { + // TODO: REMOVE + notifier.notify(update: Release(name: "UPDATESTORE", prerelease: false, html_url: URL(string: "https://example.com")!, body: ""), ignore: nil) logger.debug("Reloading keys for store with id: \(id)") guard let store = storeList.modifiableStore, store.id == id else { throw AgentProtocolStoreNotFoundError() } try store.reload() @@ -63,19 +67,20 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol { } - class ServiceDelegate: NSObject, NSXPCListenerDelegate { +// TODO: MOVE +class ServiceDelegate: NSObject, NSXPCListenerDelegate { - let exported: AgentProtocol - - init(exportedObject: AgentProtocol) { - self.exported = exportedObject - } - - func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { - newConnection.exportedInterface = NSXPCInterface(with: AgentProtocol.self) - newConnection.exportedObject = exported - newConnection.resume() - return true - } + let exported: AgentProtocol + init(exportedObject: AgentProtocol) { + self.exported = exportedObject } + + func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { + newConnection.exportedInterface = NSXPCInterface(with: AgentProtocol.self) + newConnection.exportedObject = exported + newConnection.resume() + return true + } + +} diff --git a/Sources/Secretive/App.swift b/Sources/Secretive/App.swift index 9f69a3d..eef3dbc 100644 --- a/Sources/Secretive/App.swift +++ b/Sources/Secretive/App.swift @@ -42,7 +42,10 @@ struct Secretive: App { // Two conditions in which we reinstall/attempt a force launch: // 1: The app was just updated, and an old version of the agent is alive. Reinstall will deactivate this and activate a new one. // 2: The agent is not running for some reason. We'll attempt to reinstall it, or relaunch directly if that fails. - reinstallAgent { + reinstallAgent(uninstallFirst: agentStatusChecker.running) { + if agentStatusChecker.noninstanceSecretAgentProcesses.isEmpty { + agentLaunchController.killNonInstanceAgents(agents: agentStatusChecker.noninstanceSecretAgentProcesses) + } DispatchQueue.main.asyncAfter(deadline: .now() + 1) { agentCommunicationController.configure() } @@ -72,7 +75,7 @@ struct Secretive: App { CommandGroup(after: .help) { Button("TEST") { Task { - try await agentCommunicationController.agent.updatedStore(withID: storeList.modifiableStore?.id as? UUID ?? UUID()) + try await agentCommunicationController.agent?.updatedStore(withID: storeList.modifiableStore?.id ?? UUID()) } } } @@ -84,9 +87,9 @@ struct Secretive: App { extension Secretive { - private func reinstallAgent(completion: @escaping () -> Void) { + private func reinstallAgent(uninstallFirst: Bool, completion: @escaping () -> Void) { justUpdatedChecker.check() - agentLaunchController.install { + agentLaunchController.install(uninstallFirst: uninstallFirst) { // Wait a second for launchd to kick in (next runloop isn't enough). DispatchQueue.main.asyncAfter(deadline: .now() + 1) { agentStatusChecker.check() diff --git a/Sources/Secretive/Controllers/AgentCommunicationController.swift b/Sources/Secretive/Controllers/AgentCommunicationController.swift index 6d3d516..cfe66ca 100644 --- a/Sources/Secretive/Controllers/AgentCommunicationController.swift +++ b/Sources/Secretive/Controllers/AgentCommunicationController.swift @@ -1,34 +1,35 @@ import Foundation import Combine import AppKit +import OSLog import SecretKit import SecretAgentKitProtocol protocol AgentCommunicationControllerProtocol: ObservableObject { - var agent: AgentProtocol { get } + var agent: AgentProtocol? { get } } class AgentCommunicationController: ObservableObject, AgentCommunicationControllerProtocol { - let agent: AgentProtocol - private let connection: NSXPCConnection + private(set) var agent: AgentProtocol? = nil + private var connection: NSXPCConnection? = nil private var running = false init() { - connection = NSXPCConnection(machServiceName: Bundle.main.agentBundleID) - connection.remoteObjectInterface = NSXPCInterface(with: AgentProtocol.self) - connection.invalidationHandler = { - print("INVALID") - } - agent = connection.remoteObjectProxyWithErrorHandler({ x in - print(x) - }) as! AgentProtocol } func configure() { guard !running else { return } + connection = NSXPCConnection(machServiceName: Bundle.main.agentBundleID) + connection?.remoteObjectInterface = NSXPCInterface(with: AgentProtocol.self) + connection?.invalidationHandler = { + Logger().warning("XPC connection invalidated") + } + connection?.resume() + agent = connection?.remoteObjectProxyWithErrorHandler({ error in + Logger().error("\(String(describing: error))") + }) as! AgentProtocol running = true - connection.resume() } } diff --git a/Sources/Secretive/Controllers/AgentLaunchController.swift b/Sources/Secretive/Controllers/AgentLaunchController.swift index b82a26d..61501c8 100644 --- a/Sources/Secretive/Controllers/AgentLaunchController.swift +++ b/Sources/Secretive/Controllers/AgentLaunchController.swift @@ -6,15 +6,19 @@ import SecretKit struct AgentLaunchController { - func install(completion: (() -> Void)? = nil) { + func install(uninstallFirst: Bool = true, completion: (() -> Void)? = nil) { Logger().debug("Installing agent") - _ = setEnabled(false) + if uninstallFirst { + _ = 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 // and start new? DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { _ = setEnabled(true) + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { completion?() + } } } @@ -36,6 +40,12 @@ struct AgentLaunchController { } } + func killNonInstanceAgents(agents: [NSRunningApplication]) { + for agent in agents { + agent.terminate() + } + } + private func setEnabled(_ enabled: Bool) -> Bool { SMLoginItemSetEnabled(Bundle.main.agentBundleID as CFString, enabled) } diff --git a/Sources/Secretive/Controllers/AgentStatusChecker.swift b/Sources/Secretive/Controllers/AgentStatusChecker.swift index 8f0602e..6ff6bf0 100644 --- a/Sources/Secretive/Controllers/AgentStatusChecker.swift +++ b/Sources/Secretive/Controllers/AgentStatusChecker.swift @@ -30,13 +30,18 @@ class AgentStatusChecker: ObservableObject, AgentStatusCheckerProtocol { let agents = secretAgentProcesses for agent in agents { guard let url = agent.bundleURL else { continue } - if url.absoluteString.hasPrefix(Bundle.main.bundleURL.absoluteString) { + if url.absoluteString.contains(Bundle.main.bundleURL.absoluteString) { return agent } } return nil } + // All processes, _NOT_ including one the instance agent. + var noninstanceSecretAgentProcesses: [NSRunningApplication] { + NSRunningApplication.runningApplications(withBundleIdentifier: Bundle.main.agentBundleID) + .filter({ !($0.bundleURL?.absoluteString.contains(Bundle.main.bundleURL.absoluteString) ?? false) }) + } // Whether Secretive is being run in an Xcode environment. var developmentBuild: Bool { diff --git a/Sources/Secretive/Views/CreateSecretView.swift b/Sources/Secretive/Views/CreateSecretView.swift index fdf5b0f..6b7a590 100644 --- a/Sources/Secretive/Views/CreateSecretView.swift +++ b/Sources/Secretive/Views/CreateSecretView.swift @@ -54,7 +54,7 @@ struct CreateSecretView