diff --git a/README.md b/README.md index 80d1ced..1096fe5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,13 @@ Boot Linux VMs in a single command on macOS using the new [Virtualization.framew ## Requirements - macOS Big Sur -- Xcode 12 +- Xcode 12 (for build) + +## Install + +``` +$ brew install kendfinger/tools/virtual +``` ## Usage diff --git a/virtual.xcodeproj/project.pbxproj b/virtual.xcodeproj/project.pbxproj index 93cd0dc..702f050 100644 --- a/virtual.xcodeproj/project.pbxproj +++ b/virtual.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 2A2221922571779B00701E83 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2221912571779B00701E83 /* Constants.swift */; }; + 2A2221952571786A00701E83 /* VirtualCommandRun.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2221942571786A00701E83 /* VirtualCommandRun.swift */; }; + 2A2221992571792B00701E83 /* VirtualVersionOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2221982571792B00701E83 /* VirtualVersionOptions.swift */; }; 2ABF25AC24CF7B07001BEDC2 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF25AB24CF7B07001BEDC2 /* main.swift */; }; 2ABF25B524CF7BD4001BEDC2 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 2ABF25B424CF7BD4001BEDC2 /* ArgumentParser */; }; 2AC0F3C024D099A500880496 /* VirtualCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC0F3BF24D099A500880496 /* VirtualCommand.swift */; }; @@ -27,6 +30,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2A2221912571779B00701E83 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 2A2221942571786A00701E83 /* VirtualCommandRun.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualCommandRun.swift; sourceTree = ""; }; + 2A2221982571792B00701E83 /* VirtualVersionOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualVersionOptions.swift; sourceTree = ""; }; 2ABF25A824CF7B07001BEDC2 /* virtual */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = virtual; sourceTree = BUILT_PRODUCTS_DIR; }; 2ABF25AB24CF7B07001BEDC2 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 2ABF25B224CF7B62001BEDC2 /* virtual.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = virtual.entitlements; sourceTree = ""; }; @@ -79,8 +85,11 @@ 2ABF25B224CF7B62001BEDC2 /* virtual.entitlements */, 2ABF25B624CF85CC001BEDC2 /* Info.plist */, 2AC0F3BF24D099A500880496 /* VirtualCommand.swift */, + 2A2221942571786A00701E83 /* VirtualCommandRun.swift */, 2AC0F3C124D099C700880496 /* VirtualSystem.swift */, + 2A2221982571792B00701E83 /* VirtualVersionOptions.swift */, 2AC0F3C724D2526000880496 /* Utils.swift */, + 2A2221912571779B00701E83 /* Constants.swift */, ); path = virtual; sourceTree = ""; @@ -148,6 +157,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2A2221952571786A00701E83 /* VirtualCommandRun.swift in Sources */, + 2A2221992571792B00701E83 /* VirtualVersionOptions.swift in Sources */, + 2A2221922571779B00701E83 /* Constants.swift in Sources */, 2ABF25AC24CF7B07001BEDC2 /* main.swift in Sources */, 2AC0F3C224D099C700880496 /* VirtualSystem.swift in Sources */, 2AC0F3C024D099A500880496 /* VirtualCommand.swift in Sources */, diff --git a/virtual/Constants.swift b/virtual/Constants.swift new file mode 100644 index 0000000..8817c98 --- /dev/null +++ b/virtual/Constants.swift @@ -0,0 +1,10 @@ +// +// Constants.swift +// virtual +// +// Created by Kenneth Endfinger on 11/27/20. +// + +import Foundation + +let virtualToolVersion = "0.0.1" diff --git a/virtual/VirtualCommand.swift b/virtual/VirtualCommand.swift index 73e6a03..bbf16cb 100644 --- a/virtual/VirtualCommand.swift +++ b/virtual/VirtualCommand.swift @@ -12,53 +12,13 @@ import Virtualization struct VirtualCommand: ParsableCommand { static var configuration = CommandConfiguration( commandName: "virtual", - abstract: "Linux Virtual Machines for macOS" + abstract: "Linux Virtual Machines for macOS", + subcommands: [ + VirtualCommandRun.self + ], + defaultSubcommand: VirtualCommandRun.self ) - @Option(name: .shortAndLong, help: "Kernel Path") - var kernel: String - - @Option(name: .shortAndLong, help: "Initial Ramdisk") - var ramdisk: String = "" - - @Option(name: .shortAndLong, help: "Disk Image") - var disk: [String] = [] - - @Option(name: .shortAndLong, help: "Kernel Command Line") - var cmdline: String = "" - - @Option(name: .shortAndLong, help: "CPU Core Count") - var processors: Int = 4 - - @Option(name: .shortAndLong, help: "Machine Memory") - var memory: Int = 2048 - - @Flag(name: .shortAndLong, help: "Enable NAT Networking") - var network: Bool = false - - mutating func run() throws { - enableRawMode(fileHandle: FileHandle.standardInput) - - let system = VirtualSystem(command: self) - do { - try system.start() - - var lastState: VZVirtualMachine.State = .stopped - while system.machine != nil { - let currentState = system.machine!.state - if currentState != lastState { - NSLog("Virtual Machine State: \(system.stateToString())") - lastState = currentState - } - - if currentState == .error { - VirtualCommand.exit() - } - - sleep(1) - } - } catch { - NSLog("\(error)") - } - } + @OptionGroup() + var versionOptions: VirtualVersionOptions } diff --git a/virtual/VirtualCommandRun.swift b/virtual/VirtualCommandRun.swift new file mode 100644 index 0000000..e470e74 --- /dev/null +++ b/virtual/VirtualCommandRun.swift @@ -0,0 +1,72 @@ +// +// VirtualCommandRun.swift +// virtual +// +// Created by Kenneth Endfinger on 11/27/20. +// + +import ArgumentParser +import Foundation +import Virtualization + +struct VirtualCommandRun: ParsableCommand { + static var configuration = CommandConfiguration( + commandName: "run", + abstract: "Run a Linux Virtual Machine" + ) + + @Option(name: .shortAndLong, help: "Kernel Path") + var kernel: String + + @Option(name: .shortAndLong, help: "Initial Ramdisk") + var ramdisk: String = "" + + @Option(name: .shortAndLong, help: "Disk Image") + var disk: [String] = [] + + @Option(name: .shortAndLong, help: "Kernel Command Line") + var cmdline: String = "" + + @Option(name: .shortAndLong, help: "CPU Core Count") + var processors: Int = 4 + + @Option(name: .shortAndLong, help: "Machine Memory") + var memory: Int = 2048 + + @Flag(name: .shortAndLong, help: "Enable NAT Networking") + var network: Bool = false + + @Flag(name: .long, help: "Show Version Information") + var version: Bool = false + + mutating func run() throws { + if version { + print("virtual version \(virtualToolVersion)") + VirtualCommand.exit() + } + + enableRawMode(fileHandle: FileHandle.standardInput) + + let system = VirtualSystem(command: self) + do { + try system.start() + + var lastState: VZVirtualMachine.State = .stopped + while system.machine != nil { + let currentState = system.machine!.state + if currentState != lastState { + NSLog("Virtual Machine State: \(system.stateToString())") + lastState = currentState + } + + if currentState == .error { + VirtualCommand.exit() + } + + sleep(1) + } + } catch { + NSLog("\(error)") + } + } +} diff --git a/virtual/VirtualSystem.swift b/virtual/VirtualSystem.swift index 9d06819..80be56c 100644 --- a/virtual/VirtualSystem.swift +++ b/virtual/VirtualSystem.swift @@ -9,12 +9,12 @@ import Foundation import Virtualization class VirtualSystem: NSObject, VZVirtualMachineDelegate { - let command: VirtualCommand + let command: VirtualCommandRun let queue = DispatchQueue(label: "io.endfinger.virtual.vm") var machine: VZVirtualMachine? - init(command: VirtualCommand) { + init(command: VirtualCommandRun) { self.command = command } diff --git a/virtual/VirtualVersionOptions.swift b/virtual/VirtualVersionOptions.swift new file mode 100644 index 0000000..03c0509 --- /dev/null +++ b/virtual/VirtualVersionOptions.swift @@ -0,0 +1,20 @@ +// +// VirtualVersionOptions.swift +// virtual +// +// Created by Kenneth Endfinger on 11/27/20. +// + +import ArgumentParser + +struct VirtualVersionOptions: ParsableArguments { + @Flag(name: .long, help: "Show the Tool Version") + var version: Bool = false + + func validate() throws { + if version { + print(virtualToolVersion) + throw ExitCode.success + } + } +}