Skip to content

Commit a295443

Browse files
committed
Vminitd: Add pause command
Due to us supporting a pod type now, and pid ns sharing being quite a common thing for pods, lets add a pause container like command to vminitd to eventually enable pid ns sharing between containers in our variant of a pod. This changes vminitd slightly to have pause and init (default) commands as it seemed simpler than creating a whole new binary to include in the guest image.
1 parent 747ea99 commit a295443

File tree

4 files changed

+188
-99
lines changed

4 files changed

+188
-99
lines changed

vminitd/Sources/vminitd/Application.swift

Lines changed: 13 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -14,119 +14,34 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17-
import Containerization
18-
import ContainerizationError
19-
import ContainerizationOS
2017
import Foundation
2118
import Logging
22-
import NIOCore
23-
import NIOPosix
24-
25-
#if os(Linux)
26-
import Musl
27-
import LCShim
28-
#endif
2919

3020
@main
3121
struct Application {
32-
private static let foregroundEnvVar = "FOREGROUND"
33-
private static let vsockPort = 1024
34-
private static let standardErrorLock = NSLock()
35-
36-
private static func runInForeground(_ log: Logger) throws {
37-
log.info("running vminitd under pid1")
38-
39-
var command = Command("/sbin/vminitd")
40-
command.attrs = .init(setsid: true)
41-
command.stdin = .standardInput
42-
command.stdout = .standardOutput
43-
command.stderr = .standardError
44-
command.environment = ["\(foregroundEnvVar)=1"]
45-
46-
try command.start()
47-
_ = try command.wait()
48-
}
49-
50-
private static func adjustLimits() throws {
51-
var limits = rlimit()
52-
guard getrlimit(RLIMIT_NOFILE, &limits) == 0 else {
53-
throw POSIXError(.init(rawValue: errno)!)
54-
}
55-
limits.rlim_cur = 65536
56-
limits.rlim_max = 65536
57-
guard setrlimit(RLIMIT_NOFILE, &limits) == 0 else {
58-
throw POSIXError(.init(rawValue: errno)!)
59-
}
60-
}
22+
static let standardErrorLock = NSLock()
6123

6224
@Sendable
63-
private static func standardError(label: String) -> StreamLogHandler {
25+
static func standardError(label: String) -> StreamLogHandler {
6426
standardErrorLock.withLock {
6527
StreamLogHandler.standardError(label: label)
6628
}
6729
}
6830

6931
static func main() async throws {
7032
LoggingSystem.bootstrap(standardError)
71-
var log = Logger(label: "vminitd")
72-
73-
try adjustLimits()
74-
75-
// when running under debug mode, launch vminitd as a sub process of pid1
76-
// so that we get a chance to collect better logs and errors before pid1 exists
77-
// and the kernel panics.
78-
#if DEBUG
79-
let environment = ProcessInfo.processInfo.environment
80-
let foreground = environment[Self.foregroundEnvVar]
81-
log.info("checking for shim var \(foregroundEnvVar)=\(String(describing: foreground))")
82-
83-
if foreground == nil {
84-
try runInForeground(log)
85-
exit(0)
86-
}
87-
88-
// since we are not running as pid1 in this mode we must set ourselves
89-
// as a subpreaper so that all child processes are reaped by us and not
90-
// passed onto our parent.
91-
CZ_set_sub_reaper()
92-
#endif
93-
94-
signal(SIGPIPE, SIG_IGN)
95-
96-
// Because the sysctl rpc wouldn't make sense if this didn't always exist, we
97-
// ALWAYS mount /proc.
98-
guard Musl.mount("proc", "/proc", "proc", 0, "") == 0 else {
99-
log.error("failed to mount /proc")
100-
exit(1)
101-
}
102-
guard Musl.mount("tmpfs", "/run", "tmpfs", 0, "") == 0 else {
103-
log.error("failed to mount /run")
104-
exit(1)
105-
}
106-
try Binfmt.mount()
107-
108-
log.logLevel = .debug
109-
110-
log.info("vminitd booting")
111-
let eg = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
112-
let server = Initd(log: log, group: eg)
113-
114-
do {
115-
log.info("serving vminitd API")
116-
try await server.serve(port: vsockPort)
117-
log.info("vminitd API returned, syncing filesystems")
118-
119-
#if os(Linux)
120-
Musl.sync()
121-
#endif
122-
} catch {
123-
log.error("vminitd boot error \(error)")
124-
125-
#if os(Linux)
126-
Musl.sync()
127-
#endif
12833

129-
exit(1)
34+
// Parse command line arguments
35+
let args = CommandLine.arguments
36+
let command = args.count > 1 ? args[1] : "init"
37+
38+
switch command {
39+
case "pause":
40+
try PauseCommand.run()
41+
case "init":
42+
try await InitCommand.run()
43+
default:
44+
try await InitCommand.run()
13045
}
13146
}
13247
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//===----------------------------------------------------------------------===//
2+
// Copyright © 2025 Apple Inc. and the Containerization project authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// https://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//===----------------------------------------------------------------------===//
16+
17+
import Containerization
18+
import ContainerizationError
19+
import ContainerizationOS
20+
import Foundation
21+
import LCShim
22+
import Logging
23+
import Musl
24+
import NIOCore
25+
import NIOPosix
26+
27+
struct InitCommand {
28+
private static let foregroundEnvVar = "FOREGROUND"
29+
private static let vsockPort = 1024
30+
31+
static func run() async throws {
32+
let log = Logger(label: "vminitd")
33+
34+
try Self.adjustLimits()
35+
36+
// when running under debug mode, launch vminitd as a sub process of pid1
37+
// so that we get a chance to collect better logs and errors before pid1 exists
38+
// and the kernel panics.
39+
#if DEBUG
40+
let environment = ProcessInfo.processInfo.environment
41+
let foreground = environment[Self.foregroundEnvVar]
42+
log.info("checking for shim var \(Self.foregroundEnvVar)=\(String(describing: foreground))")
43+
44+
if foreground == nil {
45+
try Self.runInForeground(log)
46+
Musl.exit(0)
47+
}
48+
49+
// since we are not running as pid1 in this mode we must set ourselves
50+
// as a subpreaper so that all child processes are reaped by us and not
51+
// passed onto our parent.
52+
CZ_set_sub_reaper()
53+
#endif
54+
55+
signal(SIGPIPE, SIG_IGN)
56+
57+
// Because the sysctl rpc wouldn't make sense if this didn't always exist, we
58+
// ALWAYS mount /proc.
59+
guard Musl.mount("proc", "/proc", "proc", 0, "") == 0 else {
60+
log.error("failed to mount /proc")
61+
Musl.exit(1)
62+
}
63+
guard Musl.mount("tmpfs", "/run", "tmpfs", 0, "") == 0 else {
64+
log.error("failed to mount /run")
65+
Musl.exit(1)
66+
}
67+
try Binfmt.mount()
68+
69+
log.logLevel = .debug
70+
71+
log.info("vminitd booting")
72+
let eg = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
73+
let server = Initd(log: log, group: eg)
74+
75+
do {
76+
log.info("serving vminitd API")
77+
try await server.serve(port: Self.vsockPort)
78+
log.info("vminitd API returned, syncing filesystems")
79+
80+
Musl.sync()
81+
} catch {
82+
log.error("vminitd boot error \(error)")
83+
84+
Musl.sync()
85+
Musl.exit(1)
86+
}
87+
}
88+
89+
private static func runInForeground(_ log: Logger) throws {
90+
log.info("running vminitd under pid1")
91+
92+
var command = Command("/sbin/vminitd")
93+
command.attrs = .init(setsid: true)
94+
command.stdin = .standardInput
95+
command.stdout = .standardOutput
96+
command.stderr = .standardError
97+
command.environment = ["\(foregroundEnvVar)=1"]
98+
99+
try command.start()
100+
_ = try command.wait()
101+
}
102+
103+
private static func adjustLimits() throws {
104+
var limits = rlimit()
105+
guard getrlimit(RLIMIT_NOFILE, &limits) == 0 else {
106+
throw POSIXError(.init(rawValue: errno)!)
107+
}
108+
limits.rlim_cur = 65536
109+
limits.rlim_max = 65536
110+
guard setrlimit(RLIMIT_NOFILE, &limits) == 0 else {
111+
throw POSIXError(.init(rawValue: errno)!)
112+
}
113+
}
114+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===----------------------------------------------------------------------===//
2+
// Copyright © 2025 Apple Inc. and the Containerization project authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// https://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//===----------------------------------------------------------------------===//
16+
17+
import Dispatch
18+
import Logging
19+
import Musl
20+
21+
struct PauseCommand {
22+
static func run() throws {
23+
let log = Logger(label: "vminitd-pause")
24+
25+
if getpid() != 1 {
26+
log.warning("pause should be the first process")
27+
}
28+
29+
// NOTE: For whatever reason, using signal() for the below causes a swift compiler issue.
30+
// Can revert whenever that is understood.
31+
let sigintSource = DispatchSource.makeSignalSource(signal: SIGINT)
32+
sigintSource.setEventHandler {
33+
log.info("Shutting down, got SIGINT")
34+
Musl.exit(0)
35+
}
36+
sigintSource.resume()
37+
38+
let sigtermSource = DispatchSource.makeSignalSource(signal: SIGTERM)
39+
sigtermSource.setEventHandler {
40+
log.info("Shutting down, got SIGTERM")
41+
Musl.exit(0)
42+
}
43+
sigtermSource.resume()
44+
45+
let sigchldSource = DispatchSource.makeSignalSource(signal: SIGINT)
46+
sigchldSource.setEventHandler {
47+
var status: Int32 = 0
48+
while waitpid(-1, &status, WNOHANG) > 0 {}
49+
}
50+
sigchldSource.resume()
51+
52+
log.info("pause container running, waiting for signals...")
53+
54+
while true {
55+
Musl.pause()
56+
}
57+
58+
log.error("Error: infinite loop terminated")
59+
Musl.exit(42)
60+
}
61+
}

vminitd/Sources/vminitd/Server+GRPC.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import Logging
2626
import NIOCore
2727
import NIOPosix
2828
import SwiftProtobuf
29-
import _NIOFileSystem
3029

3130
private let _setenv = Foundation.setenv
3231

0 commit comments

Comments
 (0)