If you are not redirected automatically, click here.
51 | 52 | 53 | EOF 54 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/System/Property/PropertyGet.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 ArgumentParser 18 | import ContainerClient 19 | import ContainerPersistence 20 | import ContainerizationError 21 | import Foundation 22 | 23 | extension Application { 24 | public struct PropertyGet: AsyncParsableCommand { 25 | public static let configuration = CommandConfiguration( 26 | commandName: "get", 27 | abstract: "Retrieve a property value" 28 | ) 29 | 30 | @OptionGroup 31 | var global: Flags.Global 32 | 33 | @Argument(help: "The property ID") 34 | var id: String 35 | 36 | public init() {} 37 | 38 | public func run() async throws { 39 | let value = DefaultsStore.allValues() 40 | .filter { id == $0.id } 41 | .first 42 | guard let value else { 43 | throw ContainerizationError(.invalidArgument, message: "property ID \(id) not found") 44 | } 45 | 46 | guard let val = value.value?.description else { 47 | return 48 | } 49 | 50 | print(val) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Helpers/RuntimeLinux/RuntimeLinuxHelper.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 ArgumentParser 18 | import ContainerLog 19 | import ContainerVersion 20 | import Logging 21 | import OSLog 22 | 23 | @main 24 | struct RuntimeLinuxHelper: AsyncParsableCommand { 25 | static let configuration = CommandConfiguration( 26 | commandName: "container-runtime-linux", 27 | abstract: "XPC Service for managing a Linux sandbox", 28 | version: ReleaseVersion.singleLine(appName: "container-runtime-linux"), 29 | subcommands: [ 30 | Start.self 31 | ] 32 | ) 33 | 34 | package static func setupLogger(debug: Bool, metadata: [String: Logging.Logger.Metadata.Value] = [:]) -> Logging.Logger { 35 | LoggingSystem.bootstrap { label in 36 | OSLogHandler( 37 | label: label, 38 | category: "RuntimeLinuxHelper" 39 | ) 40 | } 41 | 42 | var log = Logger(label: "com.apple.container") 43 | if debug { 44 | log.logLevel = .debug 45 | } 46 | 47 | for (key, val) in metadata { 48 | log[metadataKey: key] = val 49 | } 50 | 51 | return log 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Services/ContainerAPIService/DiskUsage/DiskUsageHarness.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 ContainerClient 18 | import ContainerXPC 19 | import ContainerizationError 20 | import Foundation 21 | import Logging 22 | 23 | /// XPC harness for disk usage operations 24 | public struct DiskUsageHarness: Sendable { 25 | let log: Logger 26 | let service: DiskUsageService 27 | 28 | public init(service: DiskUsageService, log: Logger) { 29 | self.log = log 30 | self.service = service 31 | } 32 | 33 | @Sendable 34 | public func get(_ message: XPCMessage) async throws -> XPCMessage { 35 | do { 36 | let stats = try await service.calculateDiskUsage() 37 | let data = try JSONEncoder().encode(stats) 38 | 39 | let reply = message.reply() 40 | reply.set(key: .diskUsageStats, value: data) 41 | return reply 42 | } catch { 43 | log.error("failed to get disk usage", metadata: ["error": "\(error)"]) 44 | throw ContainerizationError( 45 | .internalError, 46 | message: "failed to get disk usage", 47 | cause: error 48 | ) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Builder/BuilderStop.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 ArgumentParser 18 | import ContainerClient 19 | import ContainerizationError 20 | import Foundation 21 | 22 | extension Application { 23 | public struct BuilderStop: AsyncParsableCommand { 24 | public static var configuration: CommandConfiguration { 25 | var config = CommandConfiguration() 26 | config.commandName = "stop" 27 | config.abstract = "Stop the builder container" 28 | return config 29 | } 30 | 31 | @OptionGroup 32 | var global: Flags.Global 33 | 34 | public init() {} 35 | 36 | public func run() async throws { 37 | do { 38 | let container = try await ClientContainer.get(id: "buildkit") 39 | try await container.stop() 40 | } catch { 41 | if error is ContainerizationError { 42 | if (error as? ContainerizationError)?.code == .notFound { 43 | print("builder is not running") 44 | return 45 | } 46 | } 47 | throw error 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/ContainerVersion/ReleaseVersion.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 CVersion 18 | import Foundation 19 | 20 | public struct ReleaseVersion { 21 | public static func singleLine(appName: String) -> String { 22 | var versionDetails: [String: String] = ["build": buildType()] 23 | versionDetails["commit"] = gitCommit().map { String($0.prefix(7)) } ?? "unspecified" 24 | let extras: String = versionDetails.map { "\($0): \($1)" }.sorted().joined(separator: ", ") 25 | 26 | return "\(appName) version \(version()) (\(extras))" 27 | } 28 | 29 | public static func buildType() -> String { 30 | #if DEBUG 31 | return "debug" 32 | #else 33 | return "release" 34 | #endif 35 | } 36 | 37 | public static func version() -> String { 38 | let appBundle = Bundle.appBundle(executableURL: CommandLine.executablePathUrl) 39 | let bundleVersion = appBundle?.infoDictionary?["CFBundleShortVersionString"] as? String 40 | return bundleVersion ?? get_release_version().map { String(cString: $0) } ?? "0.0.0" 41 | } 42 | 43 | public static func gitCommit() -> String? { 44 | get_git_commit().map { String(cString: $0) } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Volume/VolumeInspect.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 ArgumentParser 18 | import ContainerClient 19 | import Foundation 20 | 21 | extension Application.VolumeCommand { 22 | public struct VolumeInspect: AsyncParsableCommand { 23 | public static let configuration = CommandConfiguration( 24 | commandName: "inspect", 25 | abstract: "Display information about one or more volumes" 26 | ) 27 | 28 | @OptionGroup 29 | var global: Flags.Global 30 | 31 | @Argument(help: "Volumes to inspect") 32 | var names: [String] 33 | 34 | public init() {} 35 | 36 | public func run() async throws { 37 | var volumes: [Volume] = [] 38 | 39 | for name in names { 40 | let volume = try await ClientVolume.inspect(name) 41 | volumes.append(volume) 42 | } 43 | 44 | let encoder = JSONEncoder() 45 | encoder.outputFormatting = [.prettyPrinted, .sortedKeys] 46 | encoder.dateEncodingStrategy = .iso8601 47 | 48 | let data = try encoder.encode(volumes) 49 | print(String(data: data, encoding: .utf8)!) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/ContainerLog/StderrLogHandler.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 Foundation 18 | import Logging 19 | 20 | /// Basic log handler for where simple message output is needed, 21 | /// such as CLI commands. 22 | public struct StderrLogHandler: LogHandler { 23 | public var logLevel: Logger.Level = .info 24 | public var metadata: Logger.Metadata = [:] 25 | 26 | public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? { 27 | get { 28 | self.metadata[metadataKey] 29 | } 30 | set { 31 | self.metadata[metadataKey] = newValue 32 | } 33 | } 34 | 35 | public init() {} 36 | 37 | public func log( 38 | level: Logger.Level, 39 | message: Logger.Message, 40 | metadata: Logger.Metadata?, 41 | source: String, 42 | file: String, 43 | function: String, 44 | line: UInt 45 | ) { 46 | let messageText = message.description 47 | let data = messageText.data(using: .utf8) ?? Data() 48 | 49 | // Use a single write call for atomicity 50 | var output = data 51 | output.append("\n".data(using: .utf8)!) 52 | FileHandle.standardError.write(output) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/ContainerBuild/BuildFile.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 Foundation 18 | import Logging 19 | 20 | public struct BuildFile { 21 | /// Tries to resolve either a Dockerfile or Containerfile relative to contextDir. 22 | /// Checks for Dockerfile, then falls back to Containerfile. 23 | public static func resolvePath(contextDir: String, log: Logger? = nil) throws -> String? { 24 | // Check for Dockerfile then Containerfile in context directory 25 | let dockerfilePath = URL(filePath: contextDir).appendingPathComponent("Dockerfile").path 26 | let containerfilePath = URL(filePath: contextDir).appendingPathComponent("Containerfile").path 27 | 28 | let dockerfileExists = FileManager.default.fileExists(atPath: dockerfilePath) 29 | let containerfileExists = FileManager.default.fileExists(atPath: containerfilePath) 30 | 31 | if dockerfileExists && containerfileExists { 32 | log?.info("Detected both Dockerfile and Containerfile, choosing Dockerfile") 33 | return dockerfilePath 34 | } 35 | 36 | if dockerfileExists { 37 | return dockerfilePath 38 | } 39 | 40 | if containerfileExists { 41 | return containerfilePath 42 | } 43 | 44 | return nil 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/TerminalProgress/Int+Formatted.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 Foundation 18 | 19 | extension Int { 20 | func formattedTime() -> String { 21 | let secondsInMinute = 60 22 | let secondsInHour = secondsInMinute * 60 23 | let secondsInDay = secondsInHour * 24 24 | 25 | let days = self / secondsInDay 26 | let hours = (self % secondsInDay) / secondsInHour 27 | let minutes = (self % secondsInHour) / secondsInMinute 28 | let seconds = self % secondsInMinute 29 | 30 | var components = [String]() 31 | if days > 0 { 32 | components.append("\(days)d") 33 | } 34 | if hours > 0 || days > 0 { 35 | components.append("\(hours)h") 36 | } 37 | if minutes > 0 || hours > 0 || days > 0 { 38 | components.append("\(minutes)m") 39 | } 40 | components.append("\(seconds)s") 41 | return components.joined(separator: " ") 42 | } 43 | 44 | func formattedNumber() -> String { 45 | let formatter = NumberFormatter() 46 | formatter.numberStyle = .decimal 47 | guard let formattedNumber = formatter.string(from: NSNumber(value: self)) else { 48 | return "" 49 | } 50 | return formattedNumber 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/ContainerCommands/Network/NetworkCreate.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 ArgumentParser 18 | import ContainerClient 19 | import ContainerNetworkService 20 | import ContainerizationError 21 | import Foundation 22 | import TerminalProgress 23 | 24 | extension Application { 25 | public struct NetworkCreate: AsyncParsableCommand { 26 | public static let configuration = CommandConfiguration( 27 | commandName: "create", 28 | abstract: "Create a new network") 29 | 30 | @Option(name: .customLong("label"), help: "Set metadata for a network") 31 | var labels: [String] = [] 32 | 33 | @Option(name: .customLong("subnet"), help: "Set subnet for a network") 34 | var subnet: String? = nil 35 | 36 | @OptionGroup 37 | var global: Flags.Global 38 | 39 | @Argument(help: "Network name") 40 | var name: String 41 | 42 | public init() {} 43 | 44 | public func run() async throws { 45 | let parsedLabels = Utility.parseKeyValuePairs(labels) 46 | let config = try NetworkConfiguration(id: self.name, mode: .nat, subnet: subnet, labels: parsedLabels) 47 | let state = try await ClientNetwork.create(configuration: config) 48 | print(state.id) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/SocketForwarderTests/TCPEchoServer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the container 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 NIO 18 | 19 | struct TCPEchoServer: Sendable { 20 | private let serverAddress: SocketAddress 21 | 22 | private let eventLoopGroup: MultiThreadedEventLoopGroup 23 | 24 | public init(serverAddress: SocketAddress, eventLoopGroup: MultiThreadedEventLoopGroup) { 25 | self.serverAddress = serverAddress 26 | self.eventLoopGroup = eventLoopGroup 27 | } 28 | 29 | public func run() throws -> EventLoopFuture