Option to run in recovery mode (#91)

* Option to run in recovery mode

* Fixed typo

* Use Dynamic package to call private APIs

* Improved comment

* Move options closer to invocation
This commit is contained in:
Fedor Korotkov 2022-05-19 08:17:05 -04:00 committed by GitHub
parent 60c15e3e49
commit 0a257a1547
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 9 deletions

View File

@ -1,5 +1,14 @@
{
"pins" : [
{
"identity" : "dynamic",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mhdhejazi/Dynamic",
"state" : {
"branch" : "master",
"revision" : "772883073d044bc754d401cabb6574624eb3778f"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",

View File

@ -13,10 +13,12 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.1.2"),
.package(url: "https://github.com/pointfreeco/swift-parsing", from: "0.9.2"),
.package(url: "https://github.com/mhdhejazi/Dynamic", branch: "master"),
],
targets: [
.executableTarget(name: "tart", dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Dynamic", package: "Dynamic"),
.product(name: "Parsing", package: "swift-parsing"),
]),
.testTarget(name: "TartTests", dependencies: ["tart"])

View File

@ -13,6 +13,8 @@ struct Run: AsyncParsableCommand {
@Flag var noGraphics: Bool = false
@Flag var recovery: Bool = false
@MainActor
func run() async throws {
let vmDir = try VMStorageLocal().open(name)
@ -21,7 +23,7 @@ struct Run: AsyncParsableCommand {
await withThrowingTaskGroup(of: Void.self) { group in
group.addTask {
do {
try await vm!.run()
try await vm!.run(recovery)
Foundation.exit(0)
} catch {

View File

@ -0,0 +1,37 @@
import Foundation
import Virtualization
import Dynamic
// Kudos to @saagarjha's VirtualApple for finding about _VZVirtualMachineStartOptions
extension VZVirtualMachine {
func start(_ recovery: Bool) async throws {
if !recovery {
// just use the regular API
return try await withCheckedThrowingContinuation { continuation in
DispatchQueue.main.async {
self.start(completionHandler: { result in
continuation.resume(with: result)
})
}
}
}
// use some private stuff only for recovery
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
DispatchQueue.main.async {
let handler: @convention(block) (_ result: Any?) -> Void = { result in
if let error = result as? Error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: ())
}
}
// dynamic magic
let options = Dynamic._VZVirtualMachineStartOptions()
options.bootMacOSRecovery = recovery
Dynamic(self)._start(withOptions: options, completionHandler: handler)
}
}
}
}

View File

@ -133,14 +133,8 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
}
}
func run() async throws {
try await withCheckedThrowingContinuation { continuation in
DispatchQueue.main.async {
self.virtualMachine.start(completionHandler: { result in
continuation.resume(with: result)
})
}
}
func run(_ recovery: Bool) async throws {
try await virtualMachine.start(recovery)
await withTaskCancellationHandler(operation: {
sema.wait()