Skip to content

Commit d55cc18

Browse files
authored
Allow setting log level for vminitd (apple#772)
The `--log-level` option when running the agent sub-command for vminitd was being silently ignored cause of the way the agent is being run. As a workaround we need to read `/proc/self/cmdline` to get the right args
1 parent 7e2ae5a commit d55cc18

6 files changed

Lines changed: 133 additions & 48 deletions

File tree

Sources/Containerization/Kernel.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
import Foundation
18+
import Logging
1819

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

41+
// Sets the log level for the Agent
42+
mutating public func setAgentLogLevel(level: Logger.Level) {
43+
self.initArgs.append(contentsOf: ["--log-level", level.description])
44+
}
45+
4046
/// Additional kernel arguments.
4147
public var kernelArgs: [String]
4248
/// Additional arguments passed to the Initial Process / Agent.

Tests/ContainerizationTests/KernelTests.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
//
1818

1919
import Foundation
20+
import Logging
2021
import Testing
2122

2223
@testable import Containerization
@@ -55,4 +56,38 @@ final class KernelTests {
5556

5657
#expect(commandLine.kernelArgs == ["console=hvc0", "debug", "panic=10"])
5758
}
59+
60+
@Test func setAgentLogLevelAppendsFlagAndValue() {
61+
var commandLine = Kernel.CommandLine(initArgs: [])
62+
commandLine.setAgentLogLevel(level: .debug)
63+
#expect(commandLine.initArgs == ["--log-level", "debug"])
64+
}
65+
66+
@Test(arguments: [
67+
(Logger.Level.trace, "trace"),
68+
(.debug, "debug"),
69+
(.info, "info"),
70+
(.notice, "notice"),
71+
(.warning, "warning"),
72+
(.error, "error"),
73+
(.critical, "critical"),
74+
])
75+
func setAgentLogLevelForEachLevel(level: Logger.Level, expected: String) {
76+
var commandLine = Kernel.CommandLine(initArgs: [])
77+
commandLine.setAgentLogLevel(level: level)
78+
#expect(commandLine.initArgs == ["--log-level", expected])
79+
}
80+
81+
@Test func setAgentLogLevelPreservesExistingInitArgs() {
82+
var commandLine = Kernel.CommandLine(initArgs: ["--verbose"])
83+
commandLine.setAgentLogLevel(level: .info)
84+
#expect(commandLine.initArgs == ["--verbose", "--log-level", "info"])
85+
}
86+
87+
@Test func setAgentLogLevelDoesNotAffectKernelArgs() {
88+
var commandLine = Kernel.CommandLine(debug: true, panic: 0, initArgs: [])
89+
let kernelArgsBefore = commandLine.kernelArgs
90+
commandLine.setAgentLogLevel(level: .warning)
91+
#expect(commandLine.kernelArgs == kernelArgsBefore)
92+
}
5893
}

examples/ctr-example/Package.resolved

Lines changed: 72 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/ctr-example/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import PackageDescription
1919

20-
let scVersion = "0.26.5"
20+
let scVersion = "0.33.4"
2121

2222
let package = Package(
2323
name: "ctr-example",

vminitd/Sources/VminitdCore/Logging.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@ import Synchronization
2323

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

2828
public init() {}
2929

30+
public init(logLevel: String) {
31+
self.logLevel = logLevel
32+
}
33+
3034
public func resolvedLogLevel() -> Logger.Level {
3135
switch logLevel.lowercased() {
3236
case "trace":

vminitd/Sources/vminitd/Application.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ struct Application: AsyncParsableCommand {
5151
// so we do this synchronously before any async code runs.
5252
try mountProc()
5353

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

90+
// /proc/self/cmdline holds argv as NUL-separated bytes. Read it after
91+
// mountProc(). Returns argv minus argv[0], suitable for parseAsRoot(_:).
92+
private static func procSelfArgv() -> [String] {
93+
guard let data = try? Data(contentsOf: URL(fileURLWithPath: "/proc/self/cmdline")) else {
94+
return []
95+
}
96+
let parts = data.split(separator: 0, omittingEmptySubsequences: true)
97+
.map { String(decoding: $0, as: UTF8.self) }
98+
return Array(parts.dropFirst())
99+
}
100+
88101
private static func isProcMounted() -> Bool {
89102
guard let data = try? String(contentsOfFile: "/proc/mounts", encoding: .utf8) else {
90103
return false

0 commit comments

Comments
 (0)