mirror of https://github.com/cirruslabs/tart.git
Option to pass externally created serial console (#448)
* Option to pass externally created serial console See https://github.com/cirruslabs/tart/pull/364#issuecomment-1472111742 for details * Fixed compilation * Apply suggestions from code review Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com> --------- Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
This commit is contained in:
parent
d7561cab0b
commit
f62949b6f4
|
|
@ -17,18 +17,24 @@ struct Run: AsyncParsableCommand {
|
|||
|
||||
@Flag(help: ArgumentHelp(
|
||||
"Don't open a UI window.",
|
||||
discussion: "Useful for integrating Tart VMs into other tools.\nUse `tart ip` in order to get an IP for SSHing or VNCing into the VM."))
|
||||
discussion: "Useful for integrating Tart VMs into other tools.\nUse `tart ip` in order to get an IP for SSHing or VNCing into the VM."))
|
||||
var noGraphics: Bool = false
|
||||
|
||||
@Flag(help: ArgumentHelp(
|
||||
"Open serial console in /dev/ttySXX",
|
||||
discussion: "Useful for debugging Linux Kernel"))
|
||||
discussion: "Useful for debugging Linux Kernel."))
|
||||
var serial: Bool = false
|
||||
|
||||
@Option(help: ArgumentHelp(
|
||||
"Attach an externally created serial console",
|
||||
discussion: "Alternative to `--serial` flag for programmatic integrations."
|
||||
))
|
||||
var serialPath: String?
|
||||
|
||||
@Flag(help: "Force open a UI window, even when VNC is enabled.")
|
||||
var graphics: Bool = false
|
||||
|
||||
@Flag(help: "Boot into recovery mode")
|
||||
@Flag(help: "Boot into recovery mode")
|
||||
var recovery: Bool = false
|
||||
|
||||
@Flag(help: ArgumentHelp(
|
||||
|
|
@ -123,12 +129,30 @@ struct Run: AsyncParsableCommand {
|
|||
}
|
||||
}
|
||||
|
||||
var serialPorts: [VZSerialPortConfiguration] = []
|
||||
if serial {
|
||||
let tty_fd = createPTY()
|
||||
if (tty_fd < 0) {
|
||||
throw RuntimeError.VMConfigurationError("Failed to create PTY")
|
||||
}
|
||||
let tty_read = FileHandle.init(fileDescriptor: tty_fd)
|
||||
let tty_write = FileHandle.init(fileDescriptor: tty_fd)
|
||||
serialPorts.append(createSerialPortConfiguration(tty_read, tty_write))
|
||||
} else if serialPath != nil {
|
||||
let tty_read = FileHandle.init(forReadingAtPath: serialPath!)
|
||||
let tty_write = FileHandle.init(forWritingAtPath: serialPath!)
|
||||
if (tty_read == nil || tty_write == nil) {
|
||||
throw RuntimeError.VMConfigurationError("Failed to open PTY")
|
||||
}
|
||||
serialPorts.append(createSerialPortConfiguration(tty_read!, tty_write!))
|
||||
}
|
||||
|
||||
vm = try VM(
|
||||
vmDir: vmDir,
|
||||
network: userSpecifiedNetwork(vmDir: vmDir) ?? NetworkShared(),
|
||||
additionalDiskAttachments: additionalDiskAttachments,
|
||||
directorySharingDevices: directoryShares() + rosettaDirectoryShare(),
|
||||
serial: serial
|
||||
serialPorts: serialPorts
|
||||
)
|
||||
|
||||
let vncImpl: VNC? = try {
|
||||
|
|
@ -204,6 +228,16 @@ struct Run: AsyncParsableCommand {
|
|||
}
|
||||
}
|
||||
|
||||
private func createSerialPortConfiguration(_ tty_read: FileHandle, _ tty_write: FileHandle) -> VZVirtioConsoleDeviceSerialPortConfiguration {
|
||||
let serialPortConfiguration = VZVirtioConsoleDeviceSerialPortConfiguration()
|
||||
let serialPortAttachment = VZFileHandleSerialPortAttachment(
|
||||
fileHandleForReading: tty_read,
|
||||
fileHandleForWriting: tty_write)
|
||||
|
||||
serialPortConfiguration.attachment = serialPortAttachment
|
||||
return serialPortConfiguration
|
||||
}
|
||||
|
||||
func isInteractiveSession() -> Bool {
|
||||
isatty(STDOUT_FILENO) == 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||
network: Network = NetworkShared(),
|
||||
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = [],
|
||||
directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [],
|
||||
serial: Bool = false
|
||||
serialPorts: [VZSerialPortConfiguration] = []
|
||||
) throws {
|
||||
name = vmDir.name
|
||||
config = try VMConfig.init(fromURL: vmDir.configURL)
|
||||
|
|
@ -56,7 +56,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||
nvramURL: vmDir.nvramURL, vmConfig: config,
|
||||
network: network, additionalDiskAttachments: additionalDiskAttachments,
|
||||
directorySharingDevices: directorySharingDevices,
|
||||
serial: serial
|
||||
serialPorts: serialPorts
|
||||
)
|
||||
virtualMachine = VZVirtualMachine(configuration: configuration)
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||
network: Network = NetworkShared(),
|
||||
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment] = [],
|
||||
directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [],
|
||||
serial: Bool = false
|
||||
serialPorts: [VZSerialPortConfiguration] = []
|
||||
) async throws {
|
||||
var ipswURL = ipswURL
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||
vmConfig: config, network: network,
|
||||
additionalDiskAttachments: additionalDiskAttachments,
|
||||
directorySharingDevices: directorySharingDevices,
|
||||
serial: serial
|
||||
serialPorts: serialPorts
|
||||
)
|
||||
virtualMachine = VZVirtualMachine(configuration: configuration)
|
||||
|
||||
|
|
@ -266,7 +266,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||
network: Network = NetworkShared(),
|
||||
additionalDiskAttachments: [VZDiskImageStorageDeviceAttachment],
|
||||
directorySharingDevices: [VZDirectorySharingDeviceConfiguration],
|
||||
serial: Bool
|
||||
serialPorts: [VZSerialPortConfiguration]
|
||||
) throws -> VZVirtualMachineConfiguration {
|
||||
let configuration = VZVirtualMachineConfiguration()
|
||||
|
||||
|
|
@ -314,21 +314,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||
configuration.directorySharingDevices = directorySharingDevices
|
||||
|
||||
// Serial Port
|
||||
if serial {
|
||||
let tty_fd = createPTY()
|
||||
if(tty_fd > -1){
|
||||
let tty_read = FileHandle.init(fileDescriptor: tty_fd)
|
||||
let tty_write = FileHandle.init(fileDescriptor: tty_fd)
|
||||
|
||||
|
||||
configuration.serialPorts = [VZVirtioConsoleDeviceSerialPortConfiguration()]
|
||||
let serialPortAttachment = VZFileHandleSerialPortAttachment(
|
||||
fileHandleForReading: tty_read,
|
||||
fileHandleForWriting: tty_write)
|
||||
|
||||
configuration.serialPorts[0].attachment = serialPortAttachment
|
||||
}
|
||||
}
|
||||
configuration.serialPorts = serialPorts
|
||||
|
||||
try configuration.validate()
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ extension Error {
|
|||
}
|
||||
|
||||
enum RuntimeError : Error {
|
||||
case VMConfigurationError(_ message: String)
|
||||
case VMDoesNotExist(name: String)
|
||||
case VMMissingFiles(_ message: String)
|
||||
case VMNotRunning(_ message: String)
|
||||
|
|
@ -65,6 +66,8 @@ protocol HasExitCode {
|
|||
extension RuntimeError : CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .VMConfigurationError(let message):
|
||||
return message
|
||||
case .VMDoesNotExist(let name):
|
||||
return "the specified VM \"\(name)\" does not exist"
|
||||
case .VMMissingFiles(let message):
|
||||
|
|
|
|||
Loading…
Reference in New Issue