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 }
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
}
}

View File

@ -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()

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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 {

View File

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