Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Sources/Containerization/Kernel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//

import Foundation
import Logging

/// An object representing a Linux kernel used to boot a virtual machine.
/// In addition to a path to the kernel itself, this type stores relevant
Expand All @@ -37,6 +38,11 @@ public struct Kernel: Sendable, Codable {
self.kernelArgs.append("panic=\(level)")
}

// Sets the log level for the Agent
mutating public func setAgentLogLevel(level: Logger.Level) {
self.initArgs.append(contentsOf: ["--log-level", level.description])
}

/// Additional kernel arguments.
public var kernelArgs: [String]
/// Additional arguments passed to the Initial Process / Agent.
Expand Down
35 changes: 35 additions & 0 deletions Tests/ContainerizationTests/KernelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//

import Foundation
import Logging
import Testing

@testable import Containerization
Expand Down Expand Up @@ -55,4 +56,38 @@ final class KernelTests {

#expect(commandLine.kernelArgs == ["console=hvc0", "debug", "panic=10"])
}

@Test func setAgentLogLevelAppendsFlagAndValue() {
var commandLine = Kernel.CommandLine(initArgs: [])
commandLine.setAgentLogLevel(level: .debug)
#expect(commandLine.initArgs == ["--log-level", "debug"])
}

@Test(arguments: [
(Logger.Level.trace, "trace"),
(.debug, "debug"),
(.info, "info"),
(.notice, "notice"),
(.warning, "warning"),
(.error, "error"),
(.critical, "critical"),
])
func setAgentLogLevelForEachLevel(level: Logger.Level, expected: String) {
var commandLine = Kernel.CommandLine(initArgs: [])
commandLine.setAgentLogLevel(level: level)
#expect(commandLine.initArgs == ["--log-level", expected])
}

@Test func setAgentLogLevelPreservesExistingInitArgs() {
var commandLine = Kernel.CommandLine(initArgs: ["--verbose"])
commandLine.setAgentLogLevel(level: .info)
#expect(commandLine.initArgs == ["--verbose", "--log-level", "info"])
}

@Test func setAgentLogLevelDoesNotAffectKernelArgs() {
var commandLine = Kernel.CommandLine(debug: true, panic: 0, initArgs: [])
let kernelArgsBefore = commandLine.kernelArgs
commandLine.setAgentLogLevel(level: .warning)
#expect(commandLine.kernelArgs == kernelArgsBefore)
}
}
117 changes: 72 additions & 45 deletions examples/ctr-example/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/ctr-example/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import PackageDescription

let scVersion = "0.26.5"
let scVersion = "0.33.4"

let package = Package(
name: "ctr-example",
Expand Down
6 changes: 5 additions & 1 deletion vminitd/Sources/VminitdCore/Logging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ import Synchronization

public struct LogLevelOption: ParsableArguments {
@Option(name: .long, help: "Set the log level (trace, debug, info, notice, warning, error, critical)")
var logLevel: String = "info"
public var logLevel: String = "info"

public init() {}

public init(logLevel: String) {
self.logLevel = logLevel
}

public func resolvedLogLevel() -> Logger.Level {
switch logLevel.lowercased() {
case "trace":
Expand Down
15 changes: 14 additions & 1 deletion vminitd/Sources/vminitd/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ struct Application: AsyncParsableCommand {
// so we do this synchronously before any async code runs.
try mountProc()

var command = try parseAsRoot()
// When running as PID 1 with a Musl-static build, Swift's runtime
// captures argc/argv as empty. Recover argv from /proc/self/cmdline.
var command = try parseAsRoot(Self.procSelfArgv())
if let asyncCommand = command as? AsyncParsableCommand {
nonisolated(unsafe) var unsafeCommand = asyncCommand
try await unsafeCommand.run()
Expand Down Expand Up @@ -85,6 +87,17 @@ struct Application: AsyncParsableCommand {
try mnt.mount(createWithPerms: 0o755)
}

// /proc/self/cmdline holds argv as NUL-separated bytes. Read it after
// mountProc(). Returns argv minus argv[0], suitable for parseAsRoot(_:).
private static func procSelfArgv() -> [String] {
guard let data = try? Data(contentsOf: URL(fileURLWithPath: "/proc/self/cmdline")) else {
return []
}
let parts = data.split(separator: 0, omittingEmptySubsequences: true)
.map { String(decoding: $0, as: UTF8.self) }
return Array(parts.dropFirst())
}

private static func isProcMounted() -> Bool {
guard let data = try? String(contentsOfFile: "/proc/mounts", encoding: .utf8) else {
return false
Expand Down
Loading