From e675d255853ba4e1107e18e3172784f15bcdbc22 Mon Sep 17 00:00:00 2001 From: ajemory Date: Tue, 19 May 2026 11:48:05 -0700 Subject: [PATCH 1/4] Moving Linux runtime/plugin components out of executable path --- Package.swift | 36 ++++++++++--------- .../RuntimeLinuxHelper+Start.swift | 4 +-- .../Server/Containers/ContainersService.swift | 2 +- .../RuntimeClient}/Bundle+Log.swift | 0 .../RuntimeClient}/ExitMonitor.swift | 0 .../RuntimeClient}/InterfaceStrategy.swift | 0 .../RuntimeClient}/NetworkBootstrapInfo.swift | 0 .../RuntimeClient}/SandboxClient.swift | 0 .../RuntimeClient}/SandboxKeys.swift | 0 .../RuntimeClient}/SandboxRoutes.swift | 0 .../SandboxRuntimeConfiguration.swift | 2 ++ .../RuntimeClient}/SandboxSnapshot.swift | 0 .../Client}/LinuxRuntimeData.swift | 0 .../Server}/IsolatedInterfaceStrategy.swift | 7 ++-- .../NonisolatedInterfaceStrategy.swift | 4 +-- .../Server/SandboxService.swift | 4 +-- .../RuntimeConfigurationTests.swift | 4 +-- 17 files changed, 35 insertions(+), 28 deletions(-) rename Sources/Services/{ContainerSandboxService/Client => Runtime/RuntimeClient}/Bundle+Log.swift (100%) rename Sources/Services/{ContainerSandboxService/Client => Runtime/RuntimeClient}/ExitMonitor.swift (100%) rename Sources/Services/{ContainerSandboxService/Server => Runtime/RuntimeClient}/InterfaceStrategy.swift (100%) rename Sources/Services/{ContainerSandboxService/Client => Runtime/RuntimeClient}/NetworkBootstrapInfo.swift (100%) rename Sources/Services/{ContainerSandboxService/Client => Runtime/RuntimeClient}/SandboxClient.swift (100%) rename Sources/Services/{ContainerSandboxService/Client => Runtime/RuntimeClient}/SandboxKeys.swift (100%) rename Sources/Services/{ContainerSandboxService/Client => Runtime/RuntimeClient}/SandboxRoutes.swift (100%) rename Sources/Services/{ContainerSandboxService/Client => Runtime/RuntimeClient}/SandboxRuntimeConfiguration.swift (94%) rename Sources/Services/{ContainerSandboxService/Client => Runtime/RuntimeClient}/SandboxSnapshot.swift (100%) rename Sources/{Plugins/RuntimeLinux/Types => Services/RuntimeLinux/Client}/LinuxRuntimeData.swift (100%) rename Sources/{Plugins/RuntimeLinux => Services/RuntimeLinux/Server}/IsolatedInterfaceStrategy.swift (92%) rename Sources/{Plugins/RuntimeLinux => Services/RuntimeLinux/Server}/NonisolatedInterfaceStrategy.swift (95%) rename Sources/Services/{ContainerSandboxService => RuntimeLinux}/Server/SandboxService.swift (99%) diff --git a/Package.swift b/Package.swift index 5135fc7e5..ad5471b58 100644 --- a/Package.swift +++ b/Package.swift @@ -35,12 +35,13 @@ let package = Package( .library(name: "ContainerAPIClient", targets: ["ContainerAPIClient"]), .library(name: "ContainerImagesService", targets: ["ContainerImagesService", "ContainerImagesServiceClient"]), .library(name: "ContainerNetworkService", targets: ["ContainerNetworkService", "ContainerNetworkServiceClient"]), - .library(name: "ContainerSandboxService", targets: ["ContainerSandboxService", "ContainerSandboxServiceClient"]), + .library(name: "ContainerSandboxService", targets: ["ContainerRuntimeLinuxServer", "ContainerRuntimeClient"]), .library(name: "ContainerResource", targets: ["ContainerResource"]), .library(name: "ContainerLog", targets: ["ContainerLog"]), .library(name: "ContainerPersistence", targets: ["ContainerPersistence"]), .library(name: "ContainerPlugin", targets: ["ContainerPlugin"]), .library(name: "ContainerVersion", targets: ["ContainerVersion"]), + .library(name: "ContainerRuntimeLinuxClient", targets: ["ContainerRuntimeLinuxClient"]), .library(name: "ContainerXPC", targets: ["ContainerXPC"]), .library(name: "ContainerOS", targets: ["ContainerOS"]), .library(name: "SocketForwarder", targets: ["SocketForwarder"]), @@ -108,7 +109,8 @@ let package = Package( "ContainerPersistence", "ContainerPlugin", "ContainerResource", - "ContainerRuntimeLinuxTypes", + "ContainerRuntimeClient", + "ContainerRuntimeLinuxClient", "ContainerVersion", "ContainerXPC", "TerminalProgress", @@ -188,7 +190,7 @@ let package = Package( "ContainerPersistence", "ContainerPlugin", "ContainerResource", - "ContainerSandboxServiceClient", + "ContainerRuntimeClient", "ContainerVersion", "ContainerXPC", "TerminalProgress", @@ -200,8 +202,8 @@ let package = Package( dependencies: [ .product(name: "Containerization", package: "containerization"), "ContainerResource", - "ContainerRuntimeLinuxTypes", - "ContainerSandboxServiceClient", + "ContainerRuntimeLinuxClient", + "ContainerRuntimeClient", ] ), .target( @@ -336,9 +338,9 @@ let package = Package( path: "Sources/Services/ContainerNetworkService/Client" ), .target( - name: "ContainerRuntimeLinuxTypes", + name: "ContainerRuntimeLinuxClient", dependencies: [], - path: "Sources/Plugins/RuntimeLinux/Types" + path: "Sources/Services/RuntimeLinux/Client" ), .executableTarget( name: "container-runtime-linux", @@ -349,41 +351,43 @@ let package = Package( "ContainerLog", "ContainerPlugin", "ContainerResource", - "ContainerRuntimeLinuxTypes", - "ContainerSandboxService", - "ContainerSandboxServiceClient", + "ContainerRuntimeClient", + "ContainerRuntimeLinuxClient", + "ContainerRuntimeLinuxServer", "ContainerVersion", "ContainerXPC", ], path: "Sources/Plugins/RuntimeLinux", - exclude: ["config.toml", "Types"] + exclude: ["config.toml"] ), .target( - name: "ContainerSandboxService", + name: "ContainerRuntimeLinuxServer", dependencies: [ .product(name: "Logging", package: "swift-log"), .product(name: "Containerization", package: "containerization"), .product(name: "ContainerizationExtras", package: "containerization"), .product(name: "ContainerizationOS", package: "containerization"), .product(name: "ArgumentParser", package: "swift-argument-parser"), + "ContainerAPIClient", "ContainerNetworkServiceClient", "ContainerOS", "ContainerPersistence", "ContainerResource", - "ContainerSandboxServiceClient", + "ContainerRuntimeClient", + "ContainerRuntimeLinuxClient", "ContainerXPC", "SocketForwarder", ], - path: "Sources/Services/ContainerSandboxService/Server" + path: "Sources/Services/RuntimeLinux/Server" ), .target( - name: "ContainerSandboxServiceClient", + name: "ContainerRuntimeClient", dependencies: [ "ContainerAPIClient", "ContainerResource", "ContainerXPC", ], - path: "Sources/Services/ContainerSandboxService/Client" + path: "Sources/Services/Runtime/RuntimeClient" ), .target( name: "ContainerResource", diff --git a/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift b/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift index c79808c0c..1fee8db14 100644 --- a/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift +++ b/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift @@ -18,8 +18,8 @@ import ArgumentParser import ContainerLog import ContainerPlugin import ContainerResource -import ContainerSandboxService -import ContainerSandboxServiceClient +import ContainerRuntimeLinuxServer +import ContainerRuntimeClient import ContainerXPC import Foundation import Logging diff --git a/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift b/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift index 12f45c4dc..a5d843ba8 100644 --- a/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift +++ b/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift @@ -19,7 +19,7 @@ import ContainerAPIClient import ContainerPersistence import ContainerPlugin import ContainerResource -import ContainerSandboxServiceClient +import ContainerRuntimeClient import ContainerXPC import Containerization import ContainerizationEXT4 diff --git a/Sources/Services/ContainerSandboxService/Client/Bundle+Log.swift b/Sources/Services/Runtime/RuntimeClient/Bundle+Log.swift similarity index 100% rename from Sources/Services/ContainerSandboxService/Client/Bundle+Log.swift rename to Sources/Services/Runtime/RuntimeClient/Bundle+Log.swift diff --git a/Sources/Services/ContainerSandboxService/Client/ExitMonitor.swift b/Sources/Services/Runtime/RuntimeClient/ExitMonitor.swift similarity index 100% rename from Sources/Services/ContainerSandboxService/Client/ExitMonitor.swift rename to Sources/Services/Runtime/RuntimeClient/ExitMonitor.swift diff --git a/Sources/Services/ContainerSandboxService/Server/InterfaceStrategy.swift b/Sources/Services/Runtime/RuntimeClient/InterfaceStrategy.swift similarity index 100% rename from Sources/Services/ContainerSandboxService/Server/InterfaceStrategy.swift rename to Sources/Services/Runtime/RuntimeClient/InterfaceStrategy.swift diff --git a/Sources/Services/ContainerSandboxService/Client/NetworkBootstrapInfo.swift b/Sources/Services/Runtime/RuntimeClient/NetworkBootstrapInfo.swift similarity index 100% rename from Sources/Services/ContainerSandboxService/Client/NetworkBootstrapInfo.swift rename to Sources/Services/Runtime/RuntimeClient/NetworkBootstrapInfo.swift diff --git a/Sources/Services/ContainerSandboxService/Client/SandboxClient.swift b/Sources/Services/Runtime/RuntimeClient/SandboxClient.swift similarity index 100% rename from Sources/Services/ContainerSandboxService/Client/SandboxClient.swift rename to Sources/Services/Runtime/RuntimeClient/SandboxClient.swift diff --git a/Sources/Services/ContainerSandboxService/Client/SandboxKeys.swift b/Sources/Services/Runtime/RuntimeClient/SandboxKeys.swift similarity index 100% rename from Sources/Services/ContainerSandboxService/Client/SandboxKeys.swift rename to Sources/Services/Runtime/RuntimeClient/SandboxKeys.swift diff --git a/Sources/Services/ContainerSandboxService/Client/SandboxRoutes.swift b/Sources/Services/Runtime/RuntimeClient/SandboxRoutes.swift similarity index 100% rename from Sources/Services/ContainerSandboxService/Client/SandboxRoutes.swift rename to Sources/Services/Runtime/RuntimeClient/SandboxRoutes.swift diff --git a/Sources/Services/ContainerSandboxService/Client/SandboxRuntimeConfiguration.swift b/Sources/Services/Runtime/RuntimeClient/SandboxRuntimeConfiguration.swift similarity index 94% rename from Sources/Services/ContainerSandboxService/Client/SandboxRuntimeConfiguration.swift rename to Sources/Services/Runtime/RuntimeClient/SandboxRuntimeConfiguration.swift index 5863ebf54..03947dca9 100644 --- a/Sources/Services/ContainerSandboxService/Client/SandboxRuntimeConfiguration.swift +++ b/Sources/Services/Runtime/RuntimeClient/SandboxRuntimeConfiguration.swift @@ -23,6 +23,8 @@ public struct RuntimeConfiguration: Codable, Sendable { static let runtimeConfigurationFilename = "runtime-configuration.json" public let path: URL + // TODO: Remove runtime-specific fields (initialFilesystem, kernel, containerRootFilesystem). + // These should be encoded into the opaque `runtimeData` field by the CLI. public let initialFilesystem: Filesystem public let kernel: Kernel public let containerConfiguration: ContainerConfiguration? diff --git a/Sources/Services/ContainerSandboxService/Client/SandboxSnapshot.swift b/Sources/Services/Runtime/RuntimeClient/SandboxSnapshot.swift similarity index 100% rename from Sources/Services/ContainerSandboxService/Client/SandboxSnapshot.swift rename to Sources/Services/Runtime/RuntimeClient/SandboxSnapshot.swift diff --git a/Sources/Plugins/RuntimeLinux/Types/LinuxRuntimeData.swift b/Sources/Services/RuntimeLinux/Client/LinuxRuntimeData.swift similarity index 100% rename from Sources/Plugins/RuntimeLinux/Types/LinuxRuntimeData.swift rename to Sources/Services/RuntimeLinux/Client/LinuxRuntimeData.swift diff --git a/Sources/Plugins/RuntimeLinux/IsolatedInterfaceStrategy.swift b/Sources/Services/RuntimeLinux/Server/IsolatedInterfaceStrategy.swift similarity index 92% rename from Sources/Plugins/RuntimeLinux/IsolatedInterfaceStrategy.swift rename to Sources/Services/RuntimeLinux/Server/IsolatedInterfaceStrategy.swift index 490d27564..5aa0bf18d 100644 --- a/Sources/Plugins/RuntimeLinux/IsolatedInterfaceStrategy.swift +++ b/Sources/Services/RuntimeLinux/Server/IsolatedInterfaceStrategy.swift @@ -15,14 +15,17 @@ //===----------------------------------------------------------------------===// import ContainerResource -import ContainerSandboxService +import ContainerRuntimeClient import ContainerXPC import Containerization /// Isolated container network interface strategy. This strategy prohibits /// container to container networking, but it is the only approach that /// works for macOS Sequoia. -struct IsolatedInterfaceStrategy: InterfaceStrategy { +public struct IsolatedInterfaceStrategy: InterfaceStrategy { + public init() {} + + public func toInterface(attachment: Attachment, interfaceIndex: Int, additionalData: XPCMessage?) -> Interface { let ipv4Gateway = interfaceIndex == 0 ? attachment.ipv4Gateway : nil return NATInterface( diff --git a/Sources/Plugins/RuntimeLinux/NonisolatedInterfaceStrategy.swift b/Sources/Services/RuntimeLinux/Server/NonisolatedInterfaceStrategy.swift similarity index 95% rename from Sources/Plugins/RuntimeLinux/NonisolatedInterfaceStrategy.swift rename to Sources/Services/RuntimeLinux/Server/NonisolatedInterfaceStrategy.swift index cf53ed230..38c1b6764 100644 --- a/Sources/Plugins/RuntimeLinux/NonisolatedInterfaceStrategy.swift +++ b/Sources/Services/RuntimeLinux/Server/NonisolatedInterfaceStrategy.swift @@ -15,7 +15,7 @@ //===----------------------------------------------------------------------===// import ContainerResource -import ContainerSandboxService +import ContainerRuntimeClient import ContainerXPC import Containerization import ContainerizationError @@ -25,7 +25,7 @@ import vmnet /// Interface strategy for containers that use macOS's custom network feature. @available(macOS 26, *) -struct NonisolatedInterfaceStrategy: InterfaceStrategy { +public struct NonisolatedInterfaceStrategy: InterfaceStrategy { private let log: Logger public init(log: Logger) { diff --git a/Sources/Services/ContainerSandboxService/Server/SandboxService.swift b/Sources/Services/RuntimeLinux/Server/SandboxService.swift similarity index 99% rename from Sources/Services/ContainerSandboxService/Server/SandboxService.swift rename to Sources/Services/RuntimeLinux/Server/SandboxService.swift index 40826b92c..74bd48b89 100644 --- a/Sources/Services/ContainerSandboxService/Server/SandboxService.swift +++ b/Sources/Services/RuntimeLinux/Server/SandboxService.swift @@ -18,7 +18,7 @@ import ContainerNetworkServiceClient import ContainerOS import ContainerPersistence import ContainerResource -import ContainerSandboxServiceClient +import ContainerRuntimeClient import ContainerXPC import Containerization import ContainerizationError @@ -139,8 +139,6 @@ public actor SandboxService { self.log.debug("enter", metadata: ["func": "\(#function)"]) defer { self.log.debug("exit", metadata: ["func": "\(#function)"]) } - // Create the bundle if it doesn't exist yet - // Create the bundle if it doesn't exist yet if !self.bundleExists(at: self.root) { try self.createBundle() diff --git a/Tests/ContainerAPIServiceTests/RuntimeConfigurationTests.swift b/Tests/ContainerAPIServiceTests/RuntimeConfigurationTests.swift index 1871e0836..813b9c7af 100644 --- a/Tests/ContainerAPIServiceTests/RuntimeConfigurationTests.swift +++ b/Tests/ContainerAPIServiceTests/RuntimeConfigurationTests.swift @@ -15,8 +15,8 @@ //===----------------------------------------------------------------------===// import ContainerResource -import ContainerRuntimeLinuxTypes -import ContainerSandboxServiceClient +import ContainerRuntimeLinuxClient +import ContainerRuntimeClient import Containerization import Foundation import Testing From de449735779cb84491d0dba6a0d5a5887dcddb2b Mon Sep 17 00:00:00 2001 From: ajemory Date: Tue, 19 May 2026 16:33:13 -0700 Subject: [PATCH 2/4] updating filenames, types, and XPC routes --- .spi.yml | 5 +- Package.swift | 3 +- .../RuntimeLinuxHelper+Start.swift | 30 +++---- .../Server/Containers/ContainersService.swift | 14 ++-- ...andboxClient.swift => RuntimeClient.swift} | 84 +++++++++---------- ...ation.swift => RuntimeConfiguration.swift} | 0 .../{SandboxKeys.swift => RuntimeKeys.swift} | 6 +- .../Runtime/RuntimeClient/RuntimeRoutes.swift | 59 +++++++++++++ .../Runtime/RuntimeClient/SandboxRoutes.swift | 48 ----------- ...dboxService.swift => RuntimeService.swift} | 42 +++++----- scripts/make-docs.sh | 5 +- 11 files changed, 155 insertions(+), 141 deletions(-) rename Sources/Services/Runtime/RuntimeClient/{SandboxClient.swift => RuntimeClient.swift} (81%) rename Sources/Services/Runtime/RuntimeClient/{SandboxRuntimeConfiguration.swift => RuntimeConfiguration.swift} (100%) rename Sources/Services/Runtime/RuntimeClient/{SandboxKeys.swift => RuntimeKeys.swift} (93%) create mode 100644 Sources/Services/Runtime/RuntimeClient/RuntimeRoutes.swift delete mode 100644 Sources/Services/Runtime/RuntimeClient/SandboxRoutes.swift rename Sources/Services/RuntimeLinux/Server/{SandboxService.swift => RuntimeService.swift} (97%) diff --git a/.spi.yml b/.spi.yml index 491ffa597..d7f94f92f 100644 --- a/.spi.yml +++ b/.spi.yml @@ -5,8 +5,9 @@ builder: documentation_targets: - ContainerAPIService - ContainerAPIClient - - ContainerSandboxService - - ContainerSandboxServiceClient + - ContainerRuntimeClient + - ContainerRuntimeLinuxClient + - ContainerRuntimeLinuxServer - ContainerNetworkService - ContainerNetworkServiceClient - ContainerImagesService diff --git a/Package.swift b/Package.swift index ad5471b58..0e20cc30a 100644 --- a/Package.swift +++ b/Package.swift @@ -35,7 +35,8 @@ let package = Package( .library(name: "ContainerAPIClient", targets: ["ContainerAPIClient"]), .library(name: "ContainerImagesService", targets: ["ContainerImagesService", "ContainerImagesServiceClient"]), .library(name: "ContainerNetworkService", targets: ["ContainerNetworkService", "ContainerNetworkServiceClient"]), - .library(name: "ContainerSandboxService", targets: ["ContainerRuntimeLinuxServer", "ContainerRuntimeClient"]), + .library(name: "ContainerRuntimeLinuxServer", targets: ["ContainerRuntimeLinuxServer"]), + .library(name: "ContainerRuntimeClient", targets: ["ContainerRuntimeClient"]), .library(name: "ContainerResource", targets: ["ContainerResource"]), .library(name: "ContainerLog", targets: ["ContainerLog"]), .library(name: "ContainerPersistence", targets: ["ContainerPersistence"]), diff --git a/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift b/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift index 1fee8db14..d0cdc1b53 100644 --- a/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift +++ b/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift @@ -74,7 +74,7 @@ extension RuntimeLinuxHelper { log.info("configuring XPC server") nonisolated(unsafe) let anonymousConnection = xpc_connection_create(nil, nil) - let server = SandboxService( + let server = RuntimeService( root: .init(fileURLWithPath: root), interfaceStrategies: interfaceStrategies, eventLoopGroup: eventLoopGroup, @@ -85,7 +85,7 @@ extension RuntimeLinuxHelper { let endpointServer = XPCServer( identifier: machServiceLabel, routes: [ - SandboxRoutes.createEndpoint.rawValue: XPCServer.route(server.createEndpoint) + RuntimeRoutes.createEndpoint.rawValue: XPCServer.route(server.createEndpoint) ], log: log ) @@ -93,19 +93,19 @@ extension RuntimeLinuxHelper { let mainServer = XPCServer( connection: anonymousConnection, routes: [ - SandboxRoutes.bootstrap.rawValue: XPCServer.route(server.bootstrap), - SandboxRoutes.createProcess.rawValue: XPCServer.route(server.createProcess), - SandboxRoutes.state.rawValue: XPCServer.route(server.state), - SandboxRoutes.stop.rawValue: XPCServer.route(server.stop), - SandboxRoutes.kill.rawValue: XPCServer.route(server.kill), - SandboxRoutes.resize.rawValue: XPCServer.route(server.resize), - SandboxRoutes.wait.rawValue: XPCServer.route(server.wait), - SandboxRoutes.start.rawValue: XPCServer.route(server.startProcess), - SandboxRoutes.dial.rawValue: XPCServer.route(server.dial), - SandboxRoutes.shutdown.rawValue: XPCServer.route(server.shutdown), - SandboxRoutes.statistics.rawValue: XPCServer.route(server.statistics), - SandboxRoutes.copyIn.rawValue: XPCServer.route(server.copyIn), - SandboxRoutes.copyOut.rawValue: XPCServer.route(server.copyOut), + RuntimeRoutes.bootstrap.rawValue: XPCServer.route(server.bootstrap), + RuntimeRoutes.createProcess.rawValue: XPCServer.route(server.createProcess), + RuntimeRoutes.state.rawValue: XPCServer.route(server.state), + RuntimeRoutes.stop.rawValue: XPCServer.route(server.stop), + RuntimeRoutes.kill.rawValue: XPCServer.route(server.kill), + RuntimeRoutes.resize.rawValue: XPCServer.route(server.resize), + RuntimeRoutes.wait.rawValue: XPCServer.route(server.wait), + RuntimeRoutes.start.rawValue: XPCServer.route(server.startProcess), + RuntimeRoutes.dial.rawValue: XPCServer.route(server.dial), + RuntimeRoutes.shutdown.rawValue: XPCServer.route(server.shutdown), + RuntimeRoutes.statistics.rawValue: XPCServer.route(server.statistics), + RuntimeRoutes.copyIn.rawValue: XPCServer.route(server.copyIn), + RuntimeRoutes.copyOut.rawValue: XPCServer.route(server.copyOut), ], log: log ) diff --git a/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift b/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift index a5d843ba8..cb840e05c 100644 --- a/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift +++ b/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift @@ -34,11 +34,11 @@ import SystemPackage public actor ContainersService { struct ContainerState { var snapshot: ContainerSnapshot - var client: SandboxClient? = nil + var client: RuntimeClient? = nil - func getClient() throws -> SandboxClient { + func getClient() throws -> RuntimeClient { guard let client else { - var message = "no sandbox client exists" + var message = "no runtime client exists" if snapshot.status == .stopped { message += ": container is stopped" } @@ -451,18 +451,18 @@ public actor ContainersService { ) let runtime = state.snapshot.configuration.runtimeHandler - let sandboxClient = try await SandboxClient.create( + let runtimeClient = try await RuntimeClient.create( id: id, runtime: runtime ) - try await sandboxClient.bootstrap(stdio: stdio, networkBootstrapInfos: networkBootstrapInfos, dynamicEnv: dynamicEnv) + try await runtimeClient.bootstrap(stdio: stdio, networkBootstrapInfos: networkBootstrapInfos, dynamicEnv: dynamicEnv) try await self.exitMonitor.registerProcess( id: id, onExit: self.handleContainerExit ) - state.client = sandboxClient + state.client = runtimeClient await self.setContainerState(id, state, context: context) } catch { let label = Self.fullLaunchdServiceLabel( @@ -629,7 +629,7 @@ public actor ContainersService { let state = try self._getContainerState(id: id) // Stop should be idempotent. - let client: SandboxClient + let client: RuntimeClient do { client = try state.getClient() } catch { diff --git a/Sources/Services/Runtime/RuntimeClient/SandboxClient.swift b/Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift similarity index 81% rename from Sources/Services/Runtime/RuntimeClient/SandboxClient.swift rename to Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift index d3b80fa22..327a857c7 100644 --- a/Sources/Services/Runtime/RuntimeClient/SandboxClient.swift +++ b/Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift @@ -23,8 +23,8 @@ import ContainerizationOS import Foundation import TerminalProgress -/// A client for interacting with a single sandbox. -public struct SandboxClient: Sendable { +/// A client for interacting with a container runtime service instance. +public struct RuntimeClient: Sendable { static let label = "com.apple.container.runtime" public static func machServiceLabel(runtime: String, id: String) -> String { @@ -45,12 +45,12 @@ public struct SandboxClient: Sendable { self.client = client } - /// Create a SandboxClient by ID and runtime string. The returned client is ready to be used + /// Create a RuntimeClient by ID and runtime string. The returned client is ready to be used /// without additional steps. - public static func create(id: String, runtime: String, timeout: Duration = XPCClient.xpcRegistrationTimeout) async throws -> SandboxClient { + public static func create(id: String, runtime: String, timeout: Duration = XPCClient.xpcRegistrationTimeout) async throws -> RuntimeClient { let label = Self.machServiceLabel(runtime: runtime, id: id) let client = XPCClient(service: label) - let request = XPCMessage(route: SandboxRoutes.createEndpoint.rawValue) + let request = XPCMessage(route: RuntimeRoutes.createEndpoint.rawValue) let response: XPCMessage do { @@ -62,30 +62,30 @@ public struct SandboxClient: Sendable { cause: error ) } - guard let endpoint = response.endpoint(key: SandboxKeys.sandboxServiceEndpoint.rawValue) else { + guard let endpoint = response.endpoint(key: RuntimeKeys.runtimeServiceEndpoint.rawValue) else { throw ContainerizationError( .internalError, - message: "failed to get endpoint for sandbox service" + message: "failed to get endpoint for runtime service" ) } let endpointConnection = xpc_connection_create_from_endpoint(endpoint) let xpcClient = XPCClient(connection: endpointConnection, label: label) - return SandboxClient(id: id, runtime: runtime, client: xpcClient) + return RuntimeClient(id: id, runtime: runtime, client: xpcClient) } } // Runtime Methods -extension SandboxClient { +extension RuntimeClient { public func bootstrap( stdio: [FileHandle?], networkBootstrapInfos: [NetworkBootstrapInfo], dynamicEnv: [String: String] = [:] ) async throws { - let request = XPCMessage(route: SandboxRoutes.bootstrap.rawValue) + let request = XPCMessage(route: RuntimeRoutes.bootstrap.rawValue) for (i, h) in stdio.enumerated() { - let key: SandboxKeys = try { + let key: RuntimeKeys = try { switch i { case 0: .stdin case 1: .stdout @@ -102,10 +102,10 @@ extension SandboxClient { do { let dynamicEnv = try JSONEncoder().encode(dynamicEnv) - request.set(key: SandboxKeys.dynamicEnv.rawValue, value: dynamicEnv) + request.set(key: RuntimeKeys.dynamicEnv.rawValue, value: dynamicEnv) let infosData = try JSONEncoder().encode(networkBootstrapInfos) - request.set(key: SandboxKeys.networkBootstrapInfos.rawValue, value: infosData) + request.set(key: RuntimeKeys.networkBootstrapInfos.rawValue, value: infosData) try await self.client.send(request) } catch { throw ContainerizationError( @@ -117,7 +117,7 @@ extension SandboxClient { } public func state() async throws -> SandboxSnapshot { - let request = XPCMessage(route: SandboxRoutes.state.rawValue) + let request = XPCMessage(route: RuntimeRoutes.state.rawValue) let response: XPCMessage do { response = try await self.client.send(request) @@ -132,13 +132,13 @@ extension SandboxClient { } public func createProcess(_ id: String, config: ProcessConfiguration, stdio: [FileHandle?]) async throws { - let request = XPCMessage(route: SandboxRoutes.createProcess.rawValue) - request.set(key: SandboxKeys.id.rawValue, value: id) + let request = XPCMessage(route: RuntimeRoutes.createProcess.rawValue) + request.set(key: RuntimeKeys.id.rawValue, value: id) let data = try JSONEncoder().encode(config) - request.set(key: SandboxKeys.processConfig.rawValue, value: data) + request.set(key: RuntimeKeys.processConfig.rawValue, value: data) for (i, h) in stdio.enumerated() { - let key: SandboxKeys = try { + let key: RuntimeKeys = try { switch i { case 0: .stdin case 1: .stdout @@ -165,8 +165,8 @@ extension SandboxClient { } public func startProcess(_ id: String) async throws { - let request = XPCMessage(route: SandboxRoutes.start.rawValue) - request.set(key: SandboxKeys.id.rawValue, value: id) + let request = XPCMessage(route: RuntimeRoutes.start.rawValue) + request.set(key: RuntimeKeys.id.rawValue, value: id) do { try await self.client.send(request) } catch { @@ -179,10 +179,10 @@ extension SandboxClient { } public func stop(options: ContainerStopOptions) async throws { - let request = XPCMessage(route: SandboxRoutes.stop.rawValue) + let request = XPCMessage(route: RuntimeRoutes.stop.rawValue) let data = try JSONEncoder().encode(options) - request.set(key: SandboxKeys.stopOptions.rawValue, value: data) + request.set(key: RuntimeKeys.stopOptions.rawValue, value: data) do { try await self.client.send(request) @@ -196,9 +196,9 @@ extension SandboxClient { } public func kill(_ id: String, signal: Int64) async throws { - let request = XPCMessage(route: SandboxRoutes.kill.rawValue) - request.set(key: SandboxKeys.id.rawValue, value: id) - request.set(key: SandboxKeys.signal.rawValue, value: signal) + let request = XPCMessage(route: RuntimeRoutes.kill.rawValue) + request.set(key: RuntimeKeys.id.rawValue, value: id) + request.set(key: RuntimeKeys.signal.rawValue, value: signal) do { try await self.client.send(request) @@ -212,10 +212,10 @@ extension SandboxClient { } public func resize(_ id: String, size: Terminal.Size) async throws { - let request = XPCMessage(route: SandboxRoutes.resize.rawValue) - request.set(key: SandboxKeys.id.rawValue, value: id) - request.set(key: SandboxKeys.width.rawValue, value: UInt64(size.width)) - request.set(key: SandboxKeys.height.rawValue, value: UInt64(size.height)) + let request = XPCMessage(route: RuntimeRoutes.resize.rawValue) + request.set(key: RuntimeKeys.id.rawValue, value: id) + request.set(key: RuntimeKeys.width.rawValue, value: UInt64(size.width)) + request.set(key: RuntimeKeys.height.rawValue, value: UInt64(size.height)) do { try await self.client.send(request) @@ -229,8 +229,8 @@ extension SandboxClient { } public func wait(_ id: String) async throws -> ExitStatus { - let request = XPCMessage(route: SandboxRoutes.wait.rawValue) - request.set(key: SandboxKeys.id.rawValue, value: id) + let request = XPCMessage(route: RuntimeRoutes.wait.rawValue) + request.set(key: RuntimeKeys.id.rawValue, value: id) let response: XPCMessage do { @@ -242,14 +242,14 @@ extension SandboxClient { cause: error ) } - let code = response.int64(key: SandboxKeys.exitCode.rawValue) - let date = response.date(key: SandboxKeys.exitedAt.rawValue) + let code = response.int64(key: RuntimeKeys.exitCode.rawValue) + let date = response.date(key: RuntimeKeys.exitedAt.rawValue) return ExitStatus(exitCode: Int32(code), exitedAt: date) } public func dial(_ port: UInt32) async throws -> FileHandle { - let request = XPCMessage(route: SandboxRoutes.dial.rawValue) - request.set(key: SandboxKeys.port.rawValue, value: UInt64(port)) + let request = XPCMessage(route: RuntimeRoutes.dial.rawValue) + request.set(key: RuntimeKeys.port.rawValue, value: UInt64(port)) let response: XPCMessage do { @@ -261,7 +261,7 @@ extension SandboxClient { cause: error ) } - guard let fh = response.fileHandle(key: SandboxKeys.fd.rawValue) else { + guard let fh = response.fileHandle(key: RuntimeKeys.fd.rawValue) else { throw ContainerizationError( .internalError, message: "failed to get fd for vsock port \(port)" @@ -271,7 +271,7 @@ extension SandboxClient { } public func shutdown() async throws { - let request = XPCMessage(route: SandboxRoutes.shutdown.rawValue) + let request = XPCMessage(route: RuntimeRoutes.shutdown.rawValue) do { _ = try await self.client.send(request) @@ -320,7 +320,7 @@ extension SandboxClient { } public func statistics() async throws -> ContainerStats { - let request = XPCMessage(route: SandboxRoutes.statistics.rawValue) + let request = XPCMessage(route: RuntimeRoutes.statistics.rawValue) let response: XPCMessage do { @@ -333,7 +333,7 @@ extension SandboxClient { ) } - guard let data = response.dataNoCopy(key: SandboxKeys.statistics.rawValue) else { + guard let data = response.dataNoCopy(key: RuntimeKeys.statistics.rawValue) else { throw ContainerizationError( .internalError, message: "no statistics data returned" @@ -346,7 +346,7 @@ extension SandboxClient { extension XPCMessage { public func id() throws -> String { - let id = self.string(key: SandboxKeys.id.rawValue) + let id = self.string(key: RuntimeKeys.id.rawValue) guard let id else { throw ContainerizationError( .invalidArgument, @@ -357,7 +357,7 @@ extension XPCMessage { } func sandboxSnapshot() throws -> SandboxSnapshot { - let data = self.dataNoCopy(key: SandboxKeys.snapshot.rawValue) + let data = self.dataNoCopy(key: RuntimeKeys.snapshot.rawValue) guard let data else { throw ContainerizationError( .invalidArgument, @@ -368,7 +368,7 @@ extension XPCMessage { } public func networkBootstrapInfos() throws -> [NetworkBootstrapInfo] { - guard let data = self.dataNoCopy(key: SandboxKeys.networkBootstrapInfos.rawValue) else { + guard let data = self.dataNoCopy(key: RuntimeKeys.networkBootstrapInfos.rawValue) else { throw ContainerizationError(.invalidArgument, message: "missing networkBootstrapInfos in bootstrap message") } return try JSONDecoder().decode([NetworkBootstrapInfo].self, from: data) diff --git a/Sources/Services/Runtime/RuntimeClient/SandboxRuntimeConfiguration.swift b/Sources/Services/Runtime/RuntimeClient/RuntimeConfiguration.swift similarity index 100% rename from Sources/Services/Runtime/RuntimeClient/SandboxRuntimeConfiguration.swift rename to Sources/Services/Runtime/RuntimeClient/RuntimeConfiguration.swift diff --git a/Sources/Services/Runtime/RuntimeClient/SandboxKeys.swift b/Sources/Services/Runtime/RuntimeClient/RuntimeKeys.swift similarity index 93% rename from Sources/Services/Runtime/RuntimeClient/SandboxKeys.swift rename to Sources/Services/Runtime/RuntimeClient/RuntimeKeys.swift index 07eb04f60..b472d9dd1 100644 --- a/Sources/Services/Runtime/RuntimeClient/SandboxKeys.swift +++ b/Sources/Services/Runtime/RuntimeClient/RuntimeKeys.swift @@ -14,7 +14,7 @@ // limitations under the License. //===----------------------------------------------------------------------===// -public enum SandboxKeys: String { +public enum RuntimeKeys: String { /// ID key. case id /// Vsock port number key. @@ -27,8 +27,8 @@ public enum SandboxKeys: String { case fd /// Options for stopping a container key. case stopOptions - /// An endpoint to talk to a sandbox service. - case sandboxServiceEndpoint + /// An endpoint to talk to the runtime service. + case runtimeServiceEndpoint /// Process request keys. case signal diff --git a/Sources/Services/Runtime/RuntimeClient/RuntimeRoutes.swift b/Sources/Services/Runtime/RuntimeClient/RuntimeRoutes.swift new file mode 100644 index 000000000..cda604387 --- /dev/null +++ b/Sources/Services/Runtime/RuntimeClient/RuntimeRoutes.swift @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// Copyright © 2026 Apple Inc. and the container project authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//===----------------------------------------------------------------------===// + +/// XPC routes exposed by the runtime service. +public enum RuntimeRoutes: String { + // MARK: - Service lifecycle + + /// Create an XPC endpoint for communicating with the runtime service. + case createEndpoint = "com.apple.container.runtime/createEndpoint" + /// Shut down the runtime service process. Requires the sandbox to be stopped first. + case shutdown = "com.apple.container.runtime/shutdown" + + // MARK: - Sandbox lifecycle + + /// Bootstrap the sandbox: create the VM, configure networks, and boot the guest. + case bootstrap = "com.apple.container.runtime/bootstrap" + /// Stop the sandbox and all processes running inside it. + case stop = "com.apple.container.runtime/stop" + /// Return the current state of the sandbox. + case state = "com.apple.container.runtime/state" + /// Get resource usage statistics for the sandbox. + case statistics = "com.apple.container.runtime/statistics" + /// Open a vsock connection to a port inside the sandbox. + case dial = "com.apple.container.runtime/dial" + + // MARK: - Process management + + /// Register a new process inside the sandbox (used by exec). + case createProcess = "com.apple.container.runtime/createProcess" + /// Start a registered process inside the sandbox. + case start = "com.apple.container.runtime/start" + /// Send a signal to a process inside the sandbox. + case kill = "com.apple.container.runtime/kill" + /// Resize the PTY of a process inside the sandbox. + case resize = "com.apple.container.runtime/resize" + /// Wait for a process inside the sandbox to exit. + case wait = "com.apple.container.runtime/wait" + /// Execute a new process in the sandbox. + case exec = "com.apple.container.runtime/exec" + + // MARK: - File Management + /// Copy a file or directory into the container. + case copyIn = "com.apple.container.runtime/copyIn" + /// Copy a file or directory out of the container. + case copyOut = "com.apple.container.runtime/copyOut" +} diff --git a/Sources/Services/Runtime/RuntimeClient/SandboxRoutes.swift b/Sources/Services/Runtime/RuntimeClient/SandboxRoutes.swift deleted file mode 100644 index b0da30f54..000000000 --- a/Sources/Services/Runtime/RuntimeClient/SandboxRoutes.swift +++ /dev/null @@ -1,48 +0,0 @@ -//===----------------------------------------------------------------------===// -// Copyright © 2026 Apple Inc. and the container project authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//===----------------------------------------------------------------------===// - -public enum SandboxRoutes: String { - /// Create an xpc endpoint to the sandbox instance. - case createEndpoint = "com.apple.container.sandbox/createEndpoint" - /// Bootstrap the sandbox instance and create the init process. - case bootstrap = "com.apple.container.sandbox/bootstrap" - /// Create a process in the sandbox. - case createProcess = "com.apple.container.sandbox/createProcess" - /// Start a process in the sandbox. - case start = "com.apple.container.sandbox/start" - /// Stop the sandbox. - case stop = "com.apple.container.sandbox/stop" - /// Return the current state of the sandbox. - case state = "com.apple.container.sandbox/state" - /// Kill a process in the sandbox. - case kill = "com.apple.container.sandbox/kill" - /// Resize the pty of a process in the sandbox. - case resize = "com.apple.container.sandbox/resize" - /// Wait on a process in the sandbox. - case wait = "com.apple.container.sandbox/wait" - /// Execute a new process in the sandbox. - case exec = "com.apple.container.sandbox/exec" - /// Dial a vsock port in the sandbox. - case dial = "com.apple.container.sandbox/dial" - /// Shutdown the sandbox service process. - case shutdown = "com.apple.container.sandbox/shutdown" - /// Get statistics for the sandbox. - case statistics = "com.apple.container.sandbox/statistics" - /// Copy a file or directory into the container. - case copyIn = "com.apple.container.sandbox/copyIn" - /// Copy a file or directory out of the container. - case copyOut = "com.apple.container.sandbox/copyOut" -} diff --git a/Sources/Services/RuntimeLinux/Server/SandboxService.swift b/Sources/Services/RuntimeLinux/Server/RuntimeService.swift similarity index 97% rename from Sources/Services/RuntimeLinux/Server/SandboxService.swift rename to Sources/Services/RuntimeLinux/Server/RuntimeService.swift index 74bd48b89..b03627c11 100644 --- a/Sources/Services/RuntimeLinux/Server/SandboxService.swift +++ b/Sources/Services/RuntimeLinux/Server/RuntimeService.swift @@ -37,7 +37,7 @@ import struct ContainerizationOCI.Mount import struct ContainerizationOCI.Process /// An XPC service that manages the lifecycle of a single VM-backed container. -public actor SandboxService { +public actor RuntimeService { private let connection: xpc_connection_t private let root: URL private let interfaceStrategies: [NetworkPluginInfo: InterfaceStrategy] @@ -124,7 +124,7 @@ public actor SandboxService { let endpoint = xpc_endpoint_create(self.connection) let reply = message.reply() - reply.set(key: SandboxKeys.sandboxServiceEndpoint.rawValue, value: endpoint) + reply.set(key: RuntimeKeys.runtimeServiceEndpoint.rawValue, value: endpoint) return reply } @@ -363,12 +363,12 @@ public actor SandboxService { let reply = message.reply() let data = try JSONEncoder().encode(containerStats) - reply.set(key: SandboxKeys.statistics.rawValue, value: data) + reply.set(key: RuntimeKeys.statistics.rawValue, value: data) return reply } } - /// Shutdown the SandboxService. + /// Shutdown the RuntimeService. /// /// - Parameters: /// - message: An XPC message with no parameters. @@ -600,8 +600,8 @@ public actor SandboxService { case .running: let id = try message.id() let ctr = try getContainer() - let width = message.uint64(key: SandboxKeys.width.rawValue) - let height = message.uint64(key: SandboxKeys.height.rawValue) + let width = message.uint64(key: RuntimeKeys.width.rawValue) + let height = message.uint64(key: RuntimeKeys.height.rawValue) if id != ctr.container.id { guard let processInfo = self.processes[id] else { @@ -653,7 +653,7 @@ public actor SandboxService { self.log.debug("enter", metadata: ["func": "\(#function)"]) defer { self.log.debug("exit", metadata: ["func": "\(#function)"]) } - guard let id = message.string(key: SandboxKeys.id.rawValue) else { + guard let id = message.string(key: RuntimeKeys.id.rawValue) else { throw ContainerizationError(.invalidArgument, message: "missing id in wait xpc message") } @@ -661,8 +661,8 @@ public actor SandboxService { self.waitForExit(id: id, cont: cc) } let reply = message.reply() - reply.set(key: SandboxKeys.exitCode.rawValue, value: Int64(exitStatus.exitCode)) - reply.set(key: SandboxKeys.exitedAt.rawValue, value: exitStatus.exitedAt) + reply.set(key: RuntimeKeys.exitCode.rawValue, value: Int64(exitStatus.exitCode)) + reply.set(key: RuntimeKeys.exitedAt.rawValue, value: exitStatus.exitedAt) return reply } @@ -771,7 +771,7 @@ public actor SandboxService { switch self.state { case .running, .booted: - let port = message.uint64(key: SandboxKeys.port.rawValue) + let port = message.uint64(key: RuntimeKeys.port.rawValue) guard port > 0 else { throw ContainerizationError( .invalidArgument, @@ -783,7 +783,7 @@ public actor SandboxService { let fh = try await ctr.container.dialVsock(port: UInt32(port)) let reply = message.reply() - reply.set(key: SandboxKeys.fd.rawValue, value: fh) + reply.set(key: RuntimeKeys.fd.rawValue, value: fh) return reply default: throw ContainerizationError( @@ -1250,11 +1250,11 @@ public actor SandboxService { extension XPCMessage { fileprivate func signal() throws -> Int64 { - self.int64(key: SandboxKeys.signal.rawValue) + self.int64(key: RuntimeKeys.signal.rawValue) } fileprivate func stopOptions() throws -> ContainerStopOptions { - guard let data = self.dataNoCopy(key: SandboxKeys.stopOptions.rawValue) else { + guard let data = self.dataNoCopy(key: RuntimeKeys.stopOptions.rawValue) else { throw ContainerizationError(.invalidArgument, message: "empty StopOptions") } return try JSONDecoder().decode(ContainerStopOptions.self, from: data) @@ -1262,36 +1262,36 @@ extension XPCMessage { fileprivate func setState(_ state: SandboxSnapshot) throws { let data = try JSONEncoder().encode(state) - self.set(key: SandboxKeys.snapshot.rawValue, value: data) + self.set(key: RuntimeKeys.snapshot.rawValue, value: data) } fileprivate func stdio() -> [FileHandle?] { var handles = [FileHandle?](repeating: nil, count: 3) - if let stdin = self.fileHandle(key: SandboxKeys.stdin.rawValue) { + if let stdin = self.fileHandle(key: RuntimeKeys.stdin.rawValue) { handles[0] = stdin } - if let stdout = self.fileHandle(key: SandboxKeys.stdout.rawValue) { + if let stdout = self.fileHandle(key: RuntimeKeys.stdout.rawValue) { handles[1] = stdout } - if let stderr = self.fileHandle(key: SandboxKeys.stderr.rawValue) { + if let stderr = self.fileHandle(key: RuntimeKeys.stderr.rawValue) { handles[2] = stderr } return handles } fileprivate func setFileHandle(_ handle: FileHandle) { - self.set(key: SandboxKeys.fd.rawValue, value: handle) + self.set(key: RuntimeKeys.fd.rawValue, value: handle) } fileprivate func processConfig() throws -> ProcessConfiguration { - guard let data = self.dataNoCopy(key: SandboxKeys.processConfig.rawValue) else { + guard let data = self.dataNoCopy(key: RuntimeKeys.processConfig.rawValue) else { throw ContainerizationError(.invalidArgument, message: "empty process configuration") } return try JSONDecoder().decode(ProcessConfiguration.self, from: data) } fileprivate func dynamicEnv() throws -> [String: String] { - let data = self.dataNoCopy(key: SandboxKeys.dynamicEnv.rawValue) + let data = self.dataNoCopy(key: RuntimeKeys.dynamicEnv.rawValue) let dynamicEnv = try data.map { try JSONDecoder().decode([String: String].self, from: $0) } ?? [:] return dynamicEnv } @@ -1426,7 +1426,7 @@ extension FileHandle: @retroactive ReaderStream, @retroactive Writer { // MARK: State handler and bundle creation helpers -extension SandboxService { +extension RuntimeService { private func initializeWaiters(for id: String) throws { guard waiters[id] == nil else { throw ContainerizationError(.invalidState, message: "waiter for \(id) already initialized") diff --git a/scripts/make-docs.sh b/scripts/make-docs.sh index f134b81bd..d904eb0d4 100755 --- a/scripts/make-docs.sh +++ b/scripts/make-docs.sh @@ -18,8 +18,9 @@ opts+=("--allow-writing-to-directory" "$1") opts+=("generate-documentation") opts+=("--target" "ContainerAPIService") opts+=("--target" "ContainerAPIClient") -opts+=("--target" "ContainerSandboxService") -opts+=("--target" "ContainerSandboxServiceClient") +opts+=("--target" "ContainerRuntimeClient") +opts+=("--target" "ContainerRuntimeLinuxClient") +opts+=("--target" "ContainerRuntimeLinuxServer") opts+=("--target" "ContainerNetworkService") opts+=("--target" "ContainerNetworkServiceClient") opts+=("--target" "ContainerImagesService") From e8cafce02a2bf168257fbb29779f686ea45a7e9c Mon Sep 17 00:00:00 2001 From: ajemory Date: Tue, 19 May 2026 17:03:23 -0700 Subject: [PATCH 3/4] formatting changes --- Package.swift | 6 +++--- .../RuntimeLinux/RuntimeLinuxHelper+Start.swift | 2 +- .../Server/Containers/ContainersService.swift | 14 +++++++------- .../RuntimeClient/RuntimeConfiguration.swift | 7 ++++--- .../Server/IsolatedInterfaceStrategy.swift | 1 - .../RuntimeLinux/Server/RuntimeService.swift | 4 ++-- .../RuntimeConfigurationTests.swift | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Package.swift b/Package.swift index 0e20cc30a..5fed0c1a2 100644 --- a/Package.swift +++ b/Package.swift @@ -35,14 +35,14 @@ let package = Package( .library(name: "ContainerAPIClient", targets: ["ContainerAPIClient"]), .library(name: "ContainerImagesService", targets: ["ContainerImagesService", "ContainerImagesServiceClient"]), .library(name: "ContainerNetworkService", targets: ["ContainerNetworkService", "ContainerNetworkServiceClient"]), - .library(name: "ContainerRuntimeLinuxServer", targets: ["ContainerRuntimeLinuxServer"]), - .library(name: "ContainerRuntimeClient", targets: ["ContainerRuntimeClient"]), .library(name: "ContainerResource", targets: ["ContainerResource"]), .library(name: "ContainerLog", targets: ["ContainerLog"]), .library(name: "ContainerPersistence", targets: ["ContainerPersistence"]), .library(name: "ContainerPlugin", targets: ["ContainerPlugin"]), - .library(name: "ContainerVersion", targets: ["ContainerVersion"]), + .library(name: "ContainerRuntimeClient", targets: ["ContainerRuntimeClient"]), .library(name: "ContainerRuntimeLinuxClient", targets: ["ContainerRuntimeLinuxClient"]), + .library(name: "ContainerRuntimeLinuxServer", targets: ["ContainerRuntimeLinuxServer"]), + .library(name: "ContainerVersion", targets: ["ContainerVersion"]), .library(name: "ContainerXPC", targets: ["ContainerXPC"]), .library(name: "ContainerOS", targets: ["ContainerOS"]), .library(name: "SocketForwarder", targets: ["SocketForwarder"]), diff --git a/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift b/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift index d0cdc1b53..a2fa0b7db 100644 --- a/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift +++ b/Sources/Plugins/RuntimeLinux/RuntimeLinuxHelper+Start.swift @@ -18,8 +18,8 @@ import ArgumentParser import ContainerLog import ContainerPlugin import ContainerResource -import ContainerRuntimeLinuxServer import ContainerRuntimeClient +import ContainerRuntimeLinuxServer import ContainerXPC import Foundation import Logging diff --git a/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift b/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift index cb840e05c..88dafa19a 100644 --- a/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift +++ b/Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift @@ -335,7 +335,7 @@ public actor ContainersService { // to boot. We can go lower, but this is a somewhat safe threshold. Containerization // also gives a little bit extra than the user asked for to account for guest agent overhead. // - // NOTE: We could potentially leave this validation to the sandbox service(s), as + // NOTE: We could potentially leave this validation to the runtime service(s), as // it's possible there could be an implementation that can get away with a lower // amount and be perfectly safe. let minimumMemory: UInt64 = 200.mib() @@ -941,8 +941,8 @@ public actor ContainersService { await self.exitMonitor.stopTracking(id: id) - // Shutdown and deregister the sandbox service - self.log.info("shutting down sandbox service", metadata: ["id": "\(id)"]) + // Shutdown and deregister the runtime service + self.log.info("shutting down runtime service", metadata: ["id": "\(id)"]) let path = self.containerRoot.appendingPathComponent(id) let bundle = ContainerResource.Bundle(path: path) @@ -952,7 +952,7 @@ public actor ContainersService { instanceId: id ) - // Try to shutdown the client gracefully, but if the sandbox service + // Try to shutdown the client gracefully, but if the runtime service // is already dead (e.g., killed externally), we should still continue // with state cleanup. if let client = state.client { @@ -960,7 +960,7 @@ public actor ContainersService { try await client.shutdown() } catch { self.log.error( - "failed to shutdown sandbox service", + "failed to shutdown runtime service", metadata: [ "id": "\(id)", "error": "\(error)", @@ -973,10 +973,10 @@ public actor ContainersService { // the process was killed externally. do { try ServiceManager.deregister(fullServiceLabel: label) - self.log.info("deregistered sandbox service", metadata: ["id": "\(id)"]) + self.log.info("deregistered runtime service", metadata: ["id": "\(id)"]) } catch { self.log.error( - "failed to deregister sandbox service", + "failed to deregister runtime service", metadata: [ "id": "\(id)", "error": "\(error)", diff --git a/Sources/Services/Runtime/RuntimeClient/RuntimeConfiguration.swift b/Sources/Services/Runtime/RuntimeClient/RuntimeConfiguration.swift index 03947dca9..29507bb70 100644 --- a/Sources/Services/Runtime/RuntimeClient/RuntimeConfiguration.swift +++ b/Sources/Services/Runtime/RuntimeClient/RuntimeConfiguration.swift @@ -65,14 +65,15 @@ public struct RuntimeConfiguration: Codable, Sendable { public static func readRuntimeConfiguration(from runtimeConfigurationPath: URL) throws -> RuntimeConfiguration { let configurationPath = runtimeConfigurationPath.appendingPathComponent(RuntimeConfiguration.runtimeConfigurationFilename) - guard FileManager.default.fileExists(atPath: configurationPath.path) else { + let data: Data + do { + data = try Data(contentsOf: configurationPath) + } catch { throw ContainerizationError( .notFound, message: "runtime configuration file not found at path: \(configurationPath.path)" ) } - - let data = try Data(contentsOf: configurationPath) return try JSONDecoder().decode(RuntimeConfiguration.self, from: data) } } diff --git a/Sources/Services/RuntimeLinux/Server/IsolatedInterfaceStrategy.swift b/Sources/Services/RuntimeLinux/Server/IsolatedInterfaceStrategy.swift index 5aa0bf18d..d180fac5c 100644 --- a/Sources/Services/RuntimeLinux/Server/IsolatedInterfaceStrategy.swift +++ b/Sources/Services/RuntimeLinux/Server/IsolatedInterfaceStrategy.swift @@ -25,7 +25,6 @@ import Containerization public struct IsolatedInterfaceStrategy: InterfaceStrategy { public init() {} - public func toInterface(attachment: Attachment, interfaceIndex: Int, additionalData: XPCMessage?) -> Interface { let ipv4Gateway = interfaceIndex == 0 ? attachment.ipv4Gateway : nil return NATInterface( diff --git a/Sources/Services/RuntimeLinux/Server/RuntimeService.swift b/Sources/Services/RuntimeLinux/Server/RuntimeService.swift index b03627c11..8ec403a49 100644 --- a/Sources/Services/RuntimeLinux/Server/RuntimeService.swift +++ b/Sources/Services/RuntimeLinux/Server/RuntimeService.swift @@ -116,7 +116,7 @@ public actor RuntimeService { /// /// - Returns: An XPC message with the following parameters: /// - endpoint: An XPC endpoint that can be used to communicate - /// with the sandbox service. + /// with the runtime service. @Sendable public func createEndpoint(_ message: XPCMessage) async throws -> XPCMessage { self.log.debug("enter", metadata: ["func": "\(#function)"]) @@ -1506,7 +1506,7 @@ extension RuntimeService { case stopping /// Once a stop is successful, .stopping will transition to .stopped. case stopped - /// .shuttingDown will be the last state the sandbox service will ever be in. Shortly + /// .shuttingDown will be the last state the runtime service will ever be in. Shortly /// afterwards the process will exit. case shuttingDown } diff --git a/Tests/ContainerAPIServiceTests/RuntimeConfigurationTests.swift b/Tests/ContainerAPIServiceTests/RuntimeConfigurationTests.swift index 813b9c7af..a1529d311 100644 --- a/Tests/ContainerAPIServiceTests/RuntimeConfigurationTests.swift +++ b/Tests/ContainerAPIServiceTests/RuntimeConfigurationTests.swift @@ -15,8 +15,8 @@ //===----------------------------------------------------------------------===// import ContainerResource -import ContainerRuntimeLinuxClient import ContainerRuntimeClient +import ContainerRuntimeLinuxClient import Containerization import Foundation import Testing From 93a432aa5d1e6399b60fbb5c6edaed6058450649 Mon Sep 17 00:00:00 2001 From: ajemory Date: Wed, 20 May 2026 11:58:09 -0700 Subject: [PATCH 4/4] Fixing merge conflicts --- .../Runtime/RuntimeClient/RuntimeClient.swift | 18 +++++++++--------- .../RuntimeLinux/Server/RuntimeService.swift | 14 +++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift b/Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift index 327a857c7..3040902d9 100644 --- a/Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift +++ b/Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift @@ -285,11 +285,11 @@ extension RuntimeClient { } public func copyIn(source: String, destination: String, mode: UInt32, createParents: Bool = true) async throws { - let request = XPCMessage(route: SandboxRoutes.copyIn.rawValue) - request.set(key: SandboxKeys.sourcePath.rawValue, value: source) - request.set(key: SandboxKeys.destinationPath.rawValue, value: destination) - request.set(key: SandboxKeys.fileMode.rawValue, value: UInt64(mode)) - request.set(key: SandboxKeys.createParents.rawValue, value: createParents) + let request = XPCMessage(route: RuntimeRoutes.copyIn.rawValue) + request.set(key: RuntimeKeys.sourcePath.rawValue, value: source) + request.set(key: RuntimeKeys.destinationPath.rawValue, value: destination) + request.set(key: RuntimeKeys.fileMode.rawValue, value: UInt64(mode)) + request.set(key: RuntimeKeys.createParents.rawValue, value: createParents) do { try await self.client.send(request, responseTimeout: .seconds(300)) @@ -303,10 +303,10 @@ extension RuntimeClient { } public func copyOut(source: String, destination: String, createParents: Bool = true) async throws { - let request = XPCMessage(route: SandboxRoutes.copyOut.rawValue) - request.set(key: SandboxKeys.sourcePath.rawValue, value: source) - request.set(key: SandboxKeys.destinationPath.rawValue, value: destination) - request.set(key: SandboxKeys.createParents.rawValue, value: createParents) + let request = XPCMessage(route: RuntimeRoutes.copyOut.rawValue) + request.set(key: RuntimeKeys.sourcePath.rawValue, value: source) + request.set(key: RuntimeKeys.destinationPath.rawValue, value: destination) + request.set(key: RuntimeKeys.createParents.rawValue, value: createParents) do { try await self.client.send(request, responseTimeout: .seconds(300)) diff --git a/Sources/Services/RuntimeLinux/Server/RuntimeService.swift b/Sources/Services/RuntimeLinux/Server/RuntimeService.swift index 8ec403a49..005649b93 100644 --- a/Sources/Services/RuntimeLinux/Server/RuntimeService.swift +++ b/Sources/Services/RuntimeLinux/Server/RuntimeService.swift @@ -680,20 +680,20 @@ public actor RuntimeService { self.log.info("`copyIn` xpc handler") switch self.state { case .running, .booted: - guard let source = message.string(key: SandboxKeys.sourcePath.rawValue) else { + guard let source = message.string(key: RuntimeKeys.sourcePath.rawValue) else { throw ContainerizationError( .invalidArgument, message: "no source path supplied for copyIn" ) } - guard let destination = message.string(key: SandboxKeys.destinationPath.rawValue) else { + guard let destination = message.string(key: RuntimeKeys.destinationPath.rawValue) else { throw ContainerizationError( .invalidArgument, message: "no destination path supplied for copyIn" ) } - let mode = UInt32(message.uint64(key: SandboxKeys.fileMode.rawValue)) - let createParents = message.bool(key: SandboxKeys.createParents.rawValue) + let mode = UInt32(message.uint64(key: RuntimeKeys.fileMode.rawValue)) + let createParents = message.bool(key: RuntimeKeys.createParents.rawValue) let ctr = try getContainer() try await ctr.container.copyIn( @@ -725,20 +725,20 @@ public actor RuntimeService { self.log.info("`copyOut` xpc handler") switch self.state { case .running, .booted: - guard let source = message.string(key: SandboxKeys.sourcePath.rawValue) else { + guard let source = message.string(key: RuntimeKeys.sourcePath.rawValue) else { throw ContainerizationError( .invalidArgument, message: "no source path supplied for copyOut" ) } - guard let destination = message.string(key: SandboxKeys.destinationPath.rawValue) else { + guard let destination = message.string(key: RuntimeKeys.destinationPath.rawValue) else { throw ContainerizationError( .invalidArgument, message: "no destination path supplied for copyOut" ) } - let createParents = message.bool(key: SandboxKeys.createParents.rawValue) + let createParents = message.bool(key: RuntimeKeys.createParents.rawValue) let ctr = try getContainer() try await ctr.container.copyOut(