Skip to content

Commit cb3493b

Browse files
authored
vminitd: Add ability to set log level (#531)
This adds a small flag named --log-level to be able to set the log level of vminitd and the pause command. I imagine this won't be the last time we'll have to add flags to vminitd so I went ahead and converted vminitd to be a Argument Parser based binary as well.
1 parent c118376 commit cb3493b

File tree

4 files changed

+88
-40
lines changed

4 files changed

+88
-40
lines changed

vminitd/Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ let package = Package(
4848
.executableTarget(
4949
name: "vminitd",
5050
dependencies: [
51+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
5152
.product(name: "Logging", package: "swift-log"),
5253
.product(name: "Containerization", package: "containerization"),
5354
.product(name: "ContainerizationNetlink", package: "containerization"),

vminitd/Sources/vminitd/Application.swift

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,46 +14,44 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17+
import ArgumentParser
1718
import ContainerizationOS
1819
import Foundation
1920
import Logging
2021

2122
@main
22-
struct Application {
23-
static func main() async throws {
24-
LoggingSystem.bootstrap(StreamLogHandler.standardError)
25-
26-
// Parse command line arguments
27-
let args = CommandLine.arguments
28-
let command = args.count > 1 ? args[1] : "init"
23+
struct Application: AsyncParsableCommand {
24+
static let configuration = CommandConfiguration(
25+
commandName: "vminitd",
26+
abstract: "Virtual machine init daemon",
27+
version: "0.1.0",
28+
subcommands: [
29+
InitCommand.self,
30+
PauseCommand.self,
31+
],
32+
defaultSubcommand: InitCommand.self
33+
)
2934

30-
switch command {
31-
case "pause":
32-
let log = Logger(label: "pause")
33-
34-
log.info("Running pause command")
35-
try PauseCommand.run(log: log)
36-
case "init":
37-
fallthrough
38-
default:
39-
let log = Logger(label: "vminitd")
35+
static func main() async throws {
36+
// Swift has issues spawning threads if /proc isn't mounted,
37+
// so we do this synchronously before any async code runs.
38+
try mountProc()
4039

41-
log.info("Running init command")
42-
try Self.mountProc(log: log)
43-
try await InitCommand.run(log: log)
40+
var command = try parseAsRoot()
41+
if let asyncCommand = command as? AsyncParsableCommand {
42+
nonisolated(unsafe) var unsafeCommand = asyncCommand
43+
try await unsafeCommand.run()
44+
} else {
45+
try command.run()
4446
}
4547
}
4648

47-
// Swift seems like it has some fun issues trying to spawn threads if /proc isn't around, so we
48-
// do this before calling our first async function.
49-
static func mountProc(log: Logger) throws {
49+
private static func mountProc() throws {
5050
// Is it already mounted (would only be true in debug builds where we re-exec ourselves)?
5151
if isProcMounted() {
5252
return
5353
}
5454

55-
log.info("mounting /proc")
56-
5755
let mnt = ContainerizationOS.Mount(
5856
type: "proc",
5957
source: "proc",
@@ -63,7 +61,7 @@ struct Application {
6361
try mnt.mount(createWithPerms: 0o755)
6462
}
6563

66-
static func isProcMounted() -> Bool {
64+
private static func isProcMounted() -> Bool {
6765
guard let data = try? String(contentsOfFile: "/proc/mounts", encoding: .utf8) else {
6866
return false
6967
}
@@ -81,3 +79,36 @@ struct Application {
8179
return false
8280
}
8381
}
82+
83+
struct LogLevelOption: ParsableArguments {
84+
@Option(name: .long, help: "Set the log level (trace, debug, info, notice, warning, error, critical)")
85+
var logLevel: String = "info"
86+
87+
func resolvedLogLevel() -> Logger.Level {
88+
switch logLevel.lowercased() {
89+
case "trace":
90+
return .trace
91+
case "debug":
92+
return .debug
93+
case "info":
94+
return .info
95+
case "notice":
96+
return .notice
97+
case "warning":
98+
return .warning
99+
case "error":
100+
return .error
101+
case "critical":
102+
return .critical
103+
default:
104+
return .info
105+
}
106+
}
107+
}
108+
109+
func makeLogger(label: String, level: Logger.Level) -> Logger {
110+
LoggingSystem.bootstrap(StreamLogHandler.standardError)
111+
var log = Logger(label: label)
112+
log.logLevel = level
113+
return log
114+
}

vminitd/Sources/vminitd/InitCommand.swift

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17+
import ArgumentParser
1718
import Cgroup
1819
import Containerization
1920
import ContainerizationError
@@ -28,12 +29,19 @@ import Musl
2829
import LCShim
2930
#endif
3031

31-
struct InitCommand {
32+
struct InitCommand: AsyncParsableCommand {
33+
static let configuration = CommandConfiguration(
34+
commandName: "init",
35+
abstract: "Run the init daemon"
36+
)
37+
3238
private static let foregroundEnvVar = "FOREGROUND"
3339
private static let vsockPort = 1024
3440

35-
static func run(log: Logger) async throws {
36-
var log = log
41+
@OptionGroup var options: LogLevelOption
42+
43+
mutating func run() async throws {
44+
let log = makeLogger(label: "vminitd", level: options.resolvedLogLevel())
3745
try Self.adjustLimits(log)
3846

3947
// when running under debug mode, launch vminitd as a sub process of pid1
@@ -43,11 +51,11 @@ struct InitCommand {
4351
log.info("DEBUG mode active, checking FOREGROUND env var")
4452
let environment = ProcessInfo.processInfo.environment
4553
let foreground = environment[Self.foregroundEnvVar]
46-
log.info("checking for shim var \(foregroundEnvVar)=\(String(describing: foreground))")
54+
log.info("checking for shim var \(Self.foregroundEnvVar)=\(String(describing: foreground))")
4755

4856
if foreground == nil {
49-
try runInForeground(log)
50-
exit(0)
57+
try Self.runInForeground(log, logLevel: options.logLevel)
58+
_exit(0)
5159
}
5260

5361
log.info("FOREGROUND is set, running as subprocess, setting subreaper")
@@ -57,8 +65,6 @@ struct InitCommand {
5765
CZ_set_sub_reaper()
5866
#endif
5967

60-
log.logLevel = .debug
61-
6268
signal(SIGPIPE, SIG_IGN)
6369

6470
log.info("vminitd booting")
@@ -137,7 +143,7 @@ struct InitCommand {
137143

138144
do {
139145
log.info("serving vminitd API")
140-
try await server.serve(port: vsockPort)
146+
try await server.serve(port: Self.vsockPort)
141147
log.info("vminitd API returned, syncing filesystems")
142148

143149
#if os(Linux)
@@ -150,14 +156,14 @@ struct InitCommand {
150156
Musl.sync()
151157
#endif
152158

153-
exit(1)
159+
_exit(1)
154160
}
155161
}
156162

157-
private static func runInForeground(_ log: Logger) throws {
163+
private static func runInForeground(_ log: Logger, logLevel: String) throws {
158164
log.info("running vminitd under pid1")
159165

160-
var command = Command("/sbin/vminitd")
166+
var command = Command("/sbin/vminitd", arguments: ["init", "--log-level", logLevel])
161167
command.attrs = .init(setsid: true)
162168
command.stdin = .standardInput
163169
command.stdout = .standardOutput

vminitd/Sources/vminitd/PauseCommand.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,22 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17+
import ArgumentParser
1718
import Dispatch
1819
import Logging
1920
import Musl
2021

21-
struct PauseCommand {
22-
static func run(log: Logger) throws {
22+
struct PauseCommand: ParsableCommand {
23+
static let configuration = CommandConfiguration(
24+
commandName: "pause",
25+
abstract: "Run the pause container"
26+
)
27+
28+
@OptionGroup var options: LogLevelOption
29+
30+
mutating func run() throws {
31+
let log = makeLogger(label: "pause", level: options.resolvedLogLevel())
32+
2333
if getpid() != 1 {
2434
log.warning("pause should be the first process")
2535
}

0 commit comments

Comments
 (0)