diff --git a/AltStoreCore/Model/DatabaseManager.swift b/AltStoreCore/Model/DatabaseManager.swift index 9d517039..b91a7e8e 100644 --- a/AltStoreCore/Model/DatabaseManager.swift +++ b/AltStoreCore/Model/DatabaseManager.swift @@ -11,6 +11,15 @@ import CoreData import AltSign import Roxas +extension CFNotificationName +{ + fileprivate static let willAccessDatabase = CFNotificationName("com.rileytestut.AltStore.WillAccessDatabase" as CFString) +} + +private let ReceivedWillAccessDatabaseNotification: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void = { (center, observer, name, object, userInfo) in + DatabaseManager.shared.receivedWillAccessDatabaseNotification() +} + fileprivate class PersistentContainer: RSTPersistentContainer { override class func defaultDirectoryURL() -> URL @@ -43,10 +52,15 @@ public class DatabaseManager private let coordinator = NSFileCoordinator() private let coordinatorQueue = OperationQueue() + private var ignoreWillAccessDatabaseNotification = false + private init() { self.persistentContainer = PersistentContainer(name: "AltStore", bundle: Bundle(for: DatabaseManager.self)) self.persistentContainer.preferredMergePolicy = MergePolicy() + + let observer = Unmanaged.passUnretained(self).toOpaque() + CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, ReceivedWillAccessDatabaseNotification, CFNotificationName.willAccessDatabase.rawValue, nil, .deliverImmediately) } } @@ -73,6 +87,10 @@ public extension DatabaseManager guard !self.isStarted else { return finish(nil) } + // Quit any other running AltStore processes to prevent concurrent database access during and after migration. + self.ignoreWillAccessDatabaseNotification = true + CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), .willAccessDatabase, nil, nil, true) + self.migrateDatabaseToAppGroupIfNeeded { (result) in switch result { @@ -398,4 +416,14 @@ private extension DatabaseManager } } } + + func receivedWillAccessDatabaseNotification() + { + defer { self.ignoreWillAccessDatabaseNotification = false } + + // Ignore notifications sent by the current process. + guard !self.ignoreWillAccessDatabaseNotification else { return } + + exit(104) + } }