Skip to content

Commit 21a641c

Browse files
committed
Add and use container-runtime-linux start.
- Part of #653. - No need for `defaultCommand` since ContainerService startup will write a new launchd plist with the `start` subcommand included.
1 parent 07f1d60 commit 21a641c

File tree

4 files changed

+122
-86
lines changed

4 files changed

+122
-86
lines changed

Sources/ContainerCommands/System/SystemStart.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ extension Application {
3232

3333
@Option(
3434
name: .shortAndLong,
35-
help: "Application data directory",
35+
help: "Path to the root directory for application data",
3636
transform: { URL(filePath: $0) })
3737
var appRoot = ApplicationRoot.defaultURL
3838

3939
@Option(
4040
name: .long,
41-
help: "Path to the installation root directory",
41+
help: "Path to the root directory for application executables and plugins",
4242
transform: { URL(filePath: $0) })
4343
var installRoot = InstallRoot.defaultURL
4444

@@ -52,7 +52,7 @@ extension Application {
5252

5353
public func run() async throws {
5454
// Without the true path to the binary in the plist, `container-apiserver` won't launch properly.
55-
// TODO: Use plugin loader for API server.
55+
// TODO: Can we use the plugin loader to bootstrap the API server?
5656
let executableUrl = CommandLine.executablePathUrl
5757
.deletingLastPathComponent()
5858
.appendingPathComponent("container-apiserver")
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//===----------------------------------------------------------------------===//
2+
// Copyright © 2025 Apple Inc. and the container project authors. All rights reserved.
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 ArgumentParser
18+
import ContainerClient
19+
import ContainerLog
20+
import ContainerSandboxService
21+
import ContainerXPC
22+
import Foundation
23+
import Logging
24+
import NIO
25+
26+
extension RuntimeLinuxHelper {
27+
struct Start: AsyncParsableCommand {
28+
static let label = "com.apple.container.runtime.container-runtime-linux"
29+
30+
static let configuration = CommandConfiguration(
31+
commandName: "start",
32+
abstract: "Start helper for a Linux container"
33+
)
34+
35+
@Flag(name: .long, help: "Enable debug logging")
36+
var debug = false
37+
38+
@Option(name: .shortAndLong, help: "Sandbox UUID")
39+
var uuid: String
40+
41+
@Option(name: .shortAndLong, help: "Root directory for the sandbox")
42+
var root: String
43+
44+
var machServiceLabel: String {
45+
"\(Self.label).\(uuid)"
46+
}
47+
48+
func run() async throws {
49+
let commandName = Self._commandName
50+
let log = RuntimeLinuxHelper.setupLogger(debug: debug, metadata: ["uuid": "\(uuid)"])
51+
52+
log.info("starting \(commandName)")
53+
defer {
54+
log.info("stopping \(commandName)")
55+
}
56+
57+
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
58+
do {
59+
try adjustLimits()
60+
signal(SIGPIPE, SIG_IGN)
61+
62+
log.info("configuring XPC server")
63+
let interfaceStrategy: any InterfaceStrategy
64+
if #available(macOS 26, *) {
65+
interfaceStrategy = NonisolatedInterfaceStrategy(log: log)
66+
} else {
67+
interfaceStrategy = IsolatedInterfaceStrategy()
68+
}
69+
let server = SandboxService(root: .init(fileURLWithPath: root), interfaceStrategy: interfaceStrategy, eventLoopGroup: eventLoopGroup, log: log)
70+
let xpc = XPCServer(
71+
identifier: machServiceLabel,
72+
routes: [
73+
SandboxRoutes.bootstrap.rawValue: server.bootstrap,
74+
SandboxRoutes.createProcess.rawValue: server.createProcess,
75+
SandboxRoutes.state.rawValue: server.state,
76+
SandboxRoutes.stop.rawValue: server.stop,
77+
SandboxRoutes.kill.rawValue: server.kill,
78+
SandboxRoutes.resize.rawValue: server.resize,
79+
SandboxRoutes.wait.rawValue: server.wait,
80+
SandboxRoutes.start.rawValue: server.startProcess,
81+
SandboxRoutes.dial.rawValue: server.dial,
82+
],
83+
log: log
84+
)
85+
86+
log.info("starting XPC server")
87+
try await xpc.listen()
88+
} catch {
89+
log.error("\(commandName) failed", metadata: ["error": "\(error)"])
90+
try? await eventLoopGroup.shutdownGracefully()
91+
RuntimeLinuxHelper.Start.exit(withError: error)
92+
}
93+
}
94+
95+
private func adjustLimits() throws {
96+
var limits = rlimit()
97+
guard getrlimit(RLIMIT_NOFILE, &limits) == 0 else {
98+
throw POSIXError(.init(rawValue: errno)!)
99+
}
100+
limits.rlim_cur = 65536
101+
limits.rlim_max = 65536
102+
guard setrlimit(RLIMIT_NOFILE, &limits) == 0 else {
103+
throw POSIXError(.init(rawValue: errno)!)
104+
}
105+
}
106+
}
107+
}

Sources/Helpers/RuntimeLinux/RuntimeLinuxHelper.swift

Lines changed: 11 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -15,111 +15,39 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
import ArgumentParser
18-
import ContainerClient
1918
import ContainerLog
20-
import ContainerNetworkService
21-
import ContainerSandboxService
2219
import ContainerVersion
23-
import ContainerXPC
24-
import Containerization
25-
import ContainerizationError
26-
import Foundation
2720
import Logging
28-
import NIO
21+
import OSLog
2922

3023
@main
3124
struct RuntimeLinuxHelper: AsyncParsableCommand {
32-
static let label = "com.apple.container.runtime.container-runtime-linux"
33-
3425
static let configuration = CommandConfiguration(
3526
commandName: "container-runtime-linux",
3627
abstract: "XPC Service for managing a Linux sandbox",
37-
version: ReleaseVersion.singleLine(appName: "container-runtime-linux")
28+
version: ReleaseVersion.singleLine(appName: "container-runtime-linux"),
29+
subcommands: [
30+
Start.self
31+
]
3832
)
3933

40-
@Flag(name: .long, help: "Enable debug logging")
41-
var debug = false
42-
43-
@Option(name: .shortAndLong, help: "Sandbox UUID")
44-
var uuid: String
45-
46-
@Option(name: .shortAndLong, help: "Root directory for the sandbox")
47-
var root: String
48-
49-
var machServiceLabel: String {
50-
"\(Self.label).\(uuid)"
51-
}
52-
53-
func run() async throws {
54-
let commandName = Self._commandName
55-
let log = setupLogger()
56-
log.info("starting \(commandName)")
57-
defer {
58-
log.info("stopping \(commandName)")
59-
}
60-
61-
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
62-
do {
63-
try adjustLimits()
64-
signal(SIGPIPE, SIG_IGN)
65-
66-
log.info("configuring XPC server")
67-
let interfaceStrategy: any InterfaceStrategy
68-
if #available(macOS 26, *) {
69-
interfaceStrategy = NonisolatedInterfaceStrategy(log: log)
70-
} else {
71-
interfaceStrategy = IsolatedInterfaceStrategy()
72-
}
73-
let server = SandboxService(root: .init(fileURLWithPath: root), interfaceStrategy: interfaceStrategy, eventLoopGroup: eventLoopGroup, log: log)
74-
let xpc = XPCServer(
75-
identifier: machServiceLabel,
76-
routes: [
77-
SandboxRoutes.bootstrap.rawValue: server.bootstrap,
78-
SandboxRoutes.createProcess.rawValue: server.createProcess,
79-
SandboxRoutes.state.rawValue: server.state,
80-
SandboxRoutes.stop.rawValue: server.stop,
81-
SandboxRoutes.kill.rawValue: server.kill,
82-
SandboxRoutes.resize.rawValue: server.resize,
83-
SandboxRoutes.wait.rawValue: server.wait,
84-
SandboxRoutes.start.rawValue: server.startProcess,
85-
SandboxRoutes.dial.rawValue: server.dial,
86-
],
87-
log: log
88-
)
89-
90-
log.info("starting XPC server")
91-
try await xpc.listen()
92-
} catch {
93-
log.error("\(commandName) failed", metadata: ["error": "\(error)"])
94-
try? await eventLoopGroup.shutdownGracefully()
95-
RuntimeLinuxHelper.exit(withError: error)
96-
}
97-
}
98-
99-
private func setupLogger() -> Logger {
34+
package static func setupLogger(debug: Bool, metadata: [String: Logging.Logger.Metadata.Value] = [:]) -> Logging.Logger {
10035
LoggingSystem.bootstrap { label in
10136
OSLogHandler(
10237
label: label,
10338
category: "RuntimeLinuxHelper"
10439
)
10540
}
41+
10642
var log = Logger(label: "com.apple.container")
10743
if debug {
10844
log.logLevel = .debug
10945
}
110-
log[metadataKey: "uuid"] = "\(uuid)"
111-
return log
112-
}
11346

114-
private func adjustLimits() throws {
115-
var limits = rlimit()
116-
guard getrlimit(RLIMIT_NOFILE, &limits) == 0 else {
117-
throw POSIXError(.init(rawValue: errno)!)
118-
}
119-
limits.rlim_cur = 65536
120-
limits.rlim_max = 65536
121-
guard setrlimit(RLIMIT_NOFILE, &limits) == 0 else {
122-
throw POSIXError(.init(rawValue: errno)!)
47+
for (key, val) in metadata {
48+
log[metadataKey: key] = val
12349
}
50+
51+
return log
12452
}
12553
}

Sources/Services/ContainerAPIService/Containers/ContainersService.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ public actor ContainersService {
177177
path: URL
178178
) throws {
179179
let args = [
180+
"start",
180181
"--root", path.path,
181182
"--uuid", configuration.id,
182183
"--debug",

0 commit comments

Comments
 (0)