This commit is contained in:
Max Goedjen 2022-01-02 22:52:53 -08:00
parent d80d2ca656
commit df4661a718
No known key found for this signature in database
GPG Key ID: E58C21DD77B9B8E8
6 changed files with 57 additions and 33 deletions

View File

@ -45,6 +45,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol {
guard let update = update else { return } guard let update = update else { return }
self.notifier.notify(update: update, ignore: self.updater.ignore(release:)) 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() connect()
} }
@ -55,6 +57,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol {
} }
func updatedStore(withID id: UUID) async throws { 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)") logger.debug("Reloading keys for store with id: \(id)")
guard let store = storeList.modifiableStore, store.id == id else { throw AgentProtocolStoreNotFoundError() } guard let store = storeList.modifiableStore, store.id == id else { throw AgentProtocolStoreNotFoundError() }
try store.reload() try store.reload()
@ -63,6 +67,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol {
} }
// TODO: MOVE
class ServiceDelegate: NSObject, NSXPCListenerDelegate { class ServiceDelegate: NSObject, NSXPCListenerDelegate {
let exported: AgentProtocol let exported: AgentProtocol

View File

@ -42,7 +42,10 @@ struct Secretive: App {
// Two conditions in which we reinstall/attempt a force launch: // 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. // 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. // 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) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
agentCommunicationController.configure() agentCommunicationController.configure()
} }
@ -72,7 +75,7 @@ struct Secretive: App {
CommandGroup(after: .help) { CommandGroup(after: .help) {
Button("TEST") { Button("TEST") {
Task { 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 { extension Secretive {
private func reinstallAgent(completion: @escaping () -> Void) { private func reinstallAgent(uninstallFirst: Bool, completion: @escaping () -> Void) {
justUpdatedChecker.check() justUpdatedChecker.check()
agentLaunchController.install { agentLaunchController.install(uninstallFirst: uninstallFirst) {
// Wait a second for launchd to kick in (next runloop isn't enough). // Wait a second for launchd to kick in (next runloop isn't enough).
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
agentStatusChecker.check() agentStatusChecker.check()

View File

@ -1,34 +1,35 @@
import Foundation import Foundation
import Combine import Combine
import AppKit import AppKit
import OSLog
import SecretKit import SecretKit
import SecretAgentKitProtocol import SecretAgentKitProtocol
protocol AgentCommunicationControllerProtocol: ObservableObject { protocol AgentCommunicationControllerProtocol: ObservableObject {
var agent: AgentProtocol { get } var agent: AgentProtocol? { get }
} }
class AgentCommunicationController: ObservableObject, AgentCommunicationControllerProtocol { class AgentCommunicationController: ObservableObject, AgentCommunicationControllerProtocol {
let agent: AgentProtocol private(set) var agent: AgentProtocol? = nil
private let connection: NSXPCConnection private var connection: NSXPCConnection? = nil
private var running = false private var running = false
init() { 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() { func configure() {
guard !running else { return } 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 running = true
connection.resume()
} }
} }

View File

@ -6,16 +6,20 @@ import SecretKit
struct AgentLaunchController { struct AgentLaunchController {
func install(completion: (() -> Void)? = nil) { func install(uninstallFirst: Bool = true, completion: (() -> Void)? = nil) {
Logger().debug("Installing agent") Logger().debug("Installing agent")
if uninstallFirst {
_ = setEnabled(false) _ = setEnabled(false)
}
// This is definitely a bit of a "seems to work better" thing but: // 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 // Seems to more reliably hit if these are on separate runloops, otherwise it seems like it sometimes doesn't kill old
// and start new? // and start new?
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
_ = setEnabled(true) _ = setEnabled(true)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
completion?() completion?()
} }
}
} }
@ -36,6 +40,12 @@ struct AgentLaunchController {
} }
} }
func killNonInstanceAgents(agents: [NSRunningApplication]) {
for agent in agents {
agent.terminate()
}
}
private func setEnabled(_ enabled: Bool) -> Bool { private func setEnabled(_ enabled: Bool) -> Bool {
SMLoginItemSetEnabled(Bundle.main.agentBundleID as CFString, enabled) SMLoginItemSetEnabled(Bundle.main.agentBundleID as CFString, enabled)
} }

View File

@ -30,13 +30,18 @@ class AgentStatusChecker: ObservableObject, AgentStatusCheckerProtocol {
let agents = secretAgentProcesses let agents = secretAgentProcesses
for agent in agents { for agent in agents {
guard let url = agent.bundleURL else { continue } 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 agent
} }
} }
return nil 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. // Whether Secretive is being run in an Xcode environment.
var developmentBuild: Bool { var developmentBuild: Bool {

View File

@ -54,7 +54,7 @@ struct CreateSecretView<StoreType: SecretStoreModifiable, AgentCommunicationCont
func save() { func save() {
try! store.create(name: name, requiresAuthentication: requiresAuthentication) try! store.create(name: name, requiresAuthentication: requiresAuthentication)
Task { Task {
try! await agentCommunicationController.agent.updatedStore(withID: store.id) try! await agentCommunicationController.agent!.updatedStore(withID: store.id)
} }
showing = false showing = false
} }