diff --git a/Sources/tart/Commands/Run.swift b/Sources/tart/Commands/Run.swift index c241ddb..448a805 100644 --- a/Sources/tart/Commands/Run.swift +++ b/Sources/tart/Commands/Run.swift @@ -234,6 +234,9 @@ struct Run: AsyncParsableCommand { """, valueName: "file descriptor")) var netSoftnetControlFd: Int32? + @Option(help: .hidden) + var netSoftnetExtra: [String] = [] + @Option(help: ArgumentHelp("Comma-separated list of TCP ports to expose (e.g. --net-softnet-expose 2222:22,8080:80)", discussion: """ Options are comma-separated and are as follows: @@ -323,7 +326,7 @@ struct Run: AsyncParsableCommand { } // Automatically enable --net-softnet when any of its related options are specified - if netSoftnetAllow != nil || netSoftnetBlock != nil || netSoftnetExpose != nil || netSoftnetControlFd != nil { + if netSoftnetAllow != nil || netSoftnetBlock != nil || netSoftnetExpose != nil || netSoftnetControlFd != nil || !netSoftnetExtra.isEmpty { netSoftnet = true } @@ -696,6 +699,8 @@ struct Run: AsyncParsableCommand { softnetExtraArguments += ["--expose", netSoftnetExpose] } + softnetExtraArguments += netSoftnetExtra + if netSoftnet { let config = try VMConfig.init(fromURL: vmDir.configURL) diff --git a/Tests/TartTests/SoftnetControlFDTests.swift b/Tests/TartTests/SoftnetControlFDTests.swift index 25bd6df..f0dcec6 100644 --- a/Tests/TartTests/SoftnetControlFDTests.swift +++ b/Tests/TartTests/SoftnetControlFDTests.swift @@ -161,6 +161,25 @@ final class SoftnetControlFDTests: XCTestCase { ) } + func testSoftnetExtraArguments() throws { + // Prepare a temporary VM for command validation + let temporaryHome = try createTemporaryTartHome() + let previousHome = ProcessInfo.processInfo.environment["TART_HOME"] + setenv("TART_HOME", temporaryHome.path, 1) + defer { + restoreEnvironment("TART_HOME", value: previousHome) + try? FileManager.default.removeItem(at: temporaryHome) + } + + // Parse repeated options with a value containing spaces + let extra = ["--vm-ip=172.20.0.2", "--vmnet-network=opaque value with spaces"] + let command = try Run.parse(["vm"] + extra.map { "--net-softnet-extra=\($0)" }) + + // Verify Softnet is enabled and the arguments are preserved + XCTAssertTrue(command.netSoftnet) + XCTAssertEqual(command.netSoftnetExtra, extra) + } + private func createTemporaryTartHome() throws -> URL { let temporaryHome = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString) let vm = temporaryHome.appendingPathComponent("vms/vm")