Skip to content

Commit

Permalink
Option to pass externally created serial console (#448)
Browse files Browse the repository at this point in the history
* Option to pass externally created serial console

See #364 (comment) for details

* Fixed compilation

* Apply suggestions from code review

Co-authored-by: Nikolay Edigaryev <[email protected]>

---------

Co-authored-by: Nikolay Edigaryev <[email protected]>
  • Loading branch information
fkorotkov and edigaryev authored Mar 17, 2023
1 parent d7561ca commit f62949b
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 24 deletions.
42 changes: 38 additions & 4 deletions Sources/tart/Commands/Run.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down
26 changes: 6 additions & 20 deletions Sources/tart/VM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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()

Expand Down
3 changes: 3 additions & 0 deletions Sources/tart/VMStorageHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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):
Expand Down

0 comments on commit f62949b

Please sign in to comment.