diff --git a/Sources/Containerization/DNSConfiguration.swift b/Sources/Containerization/DNSConfiguration.swift index 84e4fc43..380f8ba8 100644 --- a/Sources/Containerization/DNSConfiguration.swift +++ b/Sources/Containerization/DNSConfiguration.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +/// DNS configuration for a container. The values will be used to +/// construct /etc/resolv.conf for a given container. public struct DNS: Sendable { public static let defaultNameservers = ["1.1.1.1"] diff --git a/Sources/Containerization/Image/Image.swift b/Sources/Containerization/Image/Image.swift index 1a396fa1..b09b69ac 100644 --- a/Sources/Containerization/Image/Image.swift +++ b/Sources/Containerization/Image/Image.swift @@ -26,6 +26,7 @@ import SystemPackage import ContainerizationExtras #endif +/// Type representing an OCI container image. public struct Image: Sendable { private let contentStore: ContentStore diff --git a/Sources/Containerization/Image/InitImage.swift b/Sources/Containerization/Image/InitImage.swift index 98671d75..7005831d 100644 --- a/Sources/Containerization/Image/InitImage.swift +++ b/Sources/Containerization/Image/InitImage.swift @@ -18,6 +18,9 @@ import ContainerizationError import ContainerizationOCI import Foundation +/// Data representing the image to use as the root filesystem for a virtual machine. +/// Typically this image would contain the guest agent used to facilitate container +/// workloads, as well as any extras that may be useful to have in the guest. public struct InitImage: Sendable { public var name: String { image.reference } diff --git a/Sources/Containerization/Kernel.swift b/Sources/Containerization/Kernel.swift index 183e7eb4..bae8f1c7 100644 --- a/Sources/Containerization/Kernel.swift +++ b/Sources/Containerization/Kernel.swift @@ -16,7 +16,9 @@ import Foundation -/// A kernel used to boot a sandbox. +/// An object representing a Linux kernel used to boot a virtual machine. +/// In addition to a path to the kernel itself, this type stores relevant +/// data such as the commandline to pass to the kernel, and init arguments. public struct Kernel: Sendable, Codable { /// The command line arguments passed to the kernel on boot. public struct CommandLine: Sendable, Codable { diff --git a/Sources/Containerization/LinuxProcess.swift b/Sources/Containerization/LinuxProcess.swift index 3ea40ee9..825b8395 100644 --- a/Sources/Containerization/LinuxProcess.swift +++ b/Sources/Containerization/LinuxProcess.swift @@ -163,7 +163,7 @@ public final class LinuxProcess: Sendable { } extension LinuxProcess { - func setupIO(streams: [ConnectionStream?]) async throws -> [FileHandle?] { + func setupIO(streams: [VsockConnectionStream?]) async throws -> [FileHandle?] { let handles = try await Timeout.run(seconds: 3) { await withTaskGroup(of: (Int, FileHandle?).self) { group in var results = [FileHandle?](repeating: nil, count: 3) @@ -231,7 +231,7 @@ extension LinuxProcess { public func start() async throws { let spec = self.state.withLock { $0.spec } - var streams = [ConnectionStream?](repeating: nil, count: 3) + var streams = [VsockConnectionStream?](repeating: nil, count: 3) if let stdin = self.ioSetup.stdin { streams[0] = try self.vm.listen(stdin.port) } diff --git a/Sources/Containerization/NATNetworkInterface.swift b/Sources/Containerization/NATNetworkInterface.swift index e07d6236..1f749ab1 100644 --- a/Sources/Containerization/NATNetworkInterface.swift +++ b/Sources/Containerization/NATNetworkInterface.swift @@ -22,6 +22,8 @@ import ContainerizationError import Foundation import Synchronization +/// An interface that uses NAT to provide an IP address for a given +/// container/virtual machine. @available(macOS 16, *) public final class NATNetworkInterface: Interface, Sendable { public var address: String { diff --git a/Sources/Containerization/SystemPlatform.swift b/Sources/Containerization/SystemPlatform.swift index b597837e..9f169223 100644 --- a/Sources/Containerization/SystemPlatform.swift +++ b/Sources/Containerization/SystemPlatform.swift @@ -16,6 +16,9 @@ import ContainerizationOCI +/// `SystemPlatform` describes an operating system and architecture pair. +/// This is primarily used to choose what kind of OCI image to pull from a +/// registry. public struct SystemPlatform: Sendable, Codable { public enum OS: String, CaseIterable, Sendable, Codable { case linux diff --git a/Sources/Containerization/VZVirtualMachineInstance.swift b/Sources/Containerization/VZVirtualMachineInstance.swift index a08e2206..b6d65a8a 100644 --- a/Sources/Containerization/VZVirtualMachineInstance.swift +++ b/Sources/Containerization/VZVirtualMachineInstance.swift @@ -184,8 +184,8 @@ extension VZVirtualMachineInstance { ).dupHandle() } - func listen(_ port: UInt32) throws -> ConnectionStream { - let stream = ConnectionStream(port: port) + func listen(_ port: UInt32) throws -> VsockConnectionStream { + let stream = VsockConnectionStream(port: port) let listener = VZVirtioSocketListener() listener.delegate = stream diff --git a/Sources/Containerization/VirtualMachineInstance.swift b/Sources/Containerization/VirtualMachineInstance.swift index 4c9be15c..4cd6c5b2 100644 --- a/Sources/Containerization/VirtualMachineInstance.swift +++ b/Sources/Containerization/VirtualMachineInstance.swift @@ -39,7 +39,7 @@ public protocol VirtualMachineInstance: Sendable { /// Dial a vsock port in the guest. func dial(_ port: UInt32) async throws -> FileHandle /// Listen on a host vsock port. - func listen(_ port: UInt32) throws -> ConnectionStream + func listen(_ port: UInt32) throws -> VsockConnectionStream /// Stop listening on a vsock port. func stopListen(_ port: UInt32) throws /// Start the virtual machine. diff --git a/Sources/Containerization/ConnectionStream.swift b/Sources/Containerization/VsockConnectionStream.swift similarity index 87% rename from Sources/Containerization/ConnectionStream.swift rename to Sources/Containerization/VsockConnectionStream.swift index b896b5b8..7ec57441 100644 --- a/Sources/Containerization/ConnectionStream.swift +++ b/Sources/Containerization/VsockConnectionStream.swift @@ -20,12 +20,14 @@ import Foundation import Virtualization #endif -public final class ConnectionStream: NSObject, Sendable { +/// A stream of vsock connections. +public final class VsockConnectionStream: NSObject, Sendable { /// A stream of connections dialed from the remote. public let connections: AsyncStream + /// The port the connections are for. + public let port: UInt32 private let cont: AsyncStream.Continuation - private let port: UInt32 public init(port: UInt32) { self.port = port @@ -41,7 +43,7 @@ public final class ConnectionStream: NSObject, Sendable { #if os(macOS) -extension ConnectionStream: VZVirtioSocketListenerDelegate { +extension VsockConnectionStream: VZVirtioSocketListenerDelegate { public func listener( _: VZVirtioSocketListener, shouldAcceptNewConnection conn: VZVirtioSocketConnection, from _: VZVirtioSocketDevice diff --git a/Sources/ContainerizationEXT4/EXT4.swift b/Sources/ContainerizationEXT4/EXT4.swift index 2b996f43..4e3521a9 100644 --- a/Sources/ContainerizationEXT4/EXT4.swift +++ b/Sources/ContainerizationEXT4/EXT4.swift @@ -240,7 +240,7 @@ More details can be found here https://ext4.wiki.kernel.org/index.php/Ext4_Disk_ ``` */ -/// A class for interacting with ext4 file systems. +/// A type for interacting with ext4 file systems. /// /// The `Ext4` class provides functionality to read the superblock of an existing ext4 block device /// and format a new block device with the ext4 file system. diff --git a/Sources/ContainerizationExtras/AsyncLock.swift b/Sources/ContainerizationExtras/AsyncLock.swift index 22fe7ed4..33d49d29 100644 --- a/Sources/ContainerizationExtras/AsyncLock.swift +++ b/Sources/ContainerizationExtras/AsyncLock.swift @@ -16,6 +16,10 @@ import Foundation +/// `AsyncLock` provides a familiar locking API, with the main benefit being that it +/// is safe to call async methods while holding the lock. This is primarily used in spots +/// where an actor makes sense, but we may need to ensure we don't fall victim to actor +/// reentrancy issues. public actor AsyncLock { private var busy = false private var queue: ArraySlice> = [] diff --git a/Sources/ContainerizationExtras/Timeout.swift b/Sources/ContainerizationExtras/Timeout.swift index 9049ff29..7f4c0f82 100644 --- a/Sources/ContainerizationExtras/Timeout.swift +++ b/Sources/ContainerizationExtras/Timeout.swift @@ -16,7 +16,11 @@ import Foundation +/// `Timeout` contains helpers to run an operation and error out if +/// the operation does not finish within a provided time. public struct Timeout { + /// Performs the passed in `operation` and throws a `CancellationError` if the operation + /// doesn't finish in the provided `seconds` amount. public static func run( seconds: UInt32, operation: @escaping @Sendable () async -> T diff --git a/Sources/ContainerizationIO/ReadStream.swift b/Sources/ContainerizationIO/ReadStream.swift index 6c63625b..7cf74880 100644 --- a/Sources/ContainerizationIO/ReadStream.swift +++ b/Sources/ContainerizationIO/ReadStream.swift @@ -18,6 +18,8 @@ import ContainerizationOS import Foundation import NIO +/// `ReadStream` is a utility type for streaming data from a `URL` +/// or `Data` blob. public class ReadStream { public static let bufferSize = Int(1.mib()) diff --git a/Sources/ContainerizationNetlink/NetlinkSession.swift b/Sources/ContainerizationNetlink/NetlinkSession.swift index c51a14be..4c5e0ba2 100644 --- a/Sources/ContainerizationNetlink/NetlinkSession.swift +++ b/Sources/ContainerizationNetlink/NetlinkSession.swift @@ -18,6 +18,9 @@ import ContainerizationExtras import ContainerizationOS import Logging +/// `NetlinkSession` facilitates interacting with netlink via a provided +/// `NetlinkSocket`. This is the core high level type offered to perform +/// actions to the netlink surface in the kernel. public struct NetlinkSession { private static let receiveDataLength = 65536 diff --git a/Sources/ContainerizationOCI/Bundle.swift b/Sources/ContainerizationOCI/Bundle.swift index 3fa20ac5..2e249818 100644 --- a/Sources/ContainerizationOCI/Bundle.swift +++ b/Sources/ContainerizationOCI/Bundle.swift @@ -27,6 +27,8 @@ private let _mount = Glibc.mount private let _umount = Glibc.umount2 #endif +/// `Bundle` represents an OCI runtime spec bundle for running +/// a container. public struct Bundle: Sendable { public let path: URL diff --git a/Sources/ContainerizationOCI/Client/Authentication.swift b/Sources/ContainerizationOCI/Client/Authentication.swift index 04ef9535..0c35cf25 100644 --- a/Sources/ContainerizationOCI/Client/Authentication.swift +++ b/Sources/ContainerizationOCI/Client/Authentication.swift @@ -16,6 +16,7 @@ import Foundation +/// Abstraction for returning a token needed for logging into an OCI compliant registry. public protocol Authentication: Sendable { func token() async throws -> String } diff --git a/Sources/ContainerizationOCI/Client/KeychainWrapper.swift b/Sources/ContainerizationOCI/Client/KeychainHelper.swift similarity index 78% rename from Sources/ContainerizationOCI/Client/KeychainWrapper.swift rename to Sources/ContainerizationOCI/Client/KeychainHelper.swift index 096a8957..4d29fb2a 100644 --- a/Sources/ContainerizationOCI/Client/KeychainWrapper.swift +++ b/Sources/ContainerizationOCI/Client/KeychainHelper.swift @@ -18,12 +18,14 @@ import Foundation import ContainerizationOS +/// Helper type to lookup registry related values in the macOS keychain. public struct KeychainHelper: Sendable { private let id: String public init(id: String) { self.id = id } + /// Lookup authorization data for a given registry domain. public func lookup(domain: String) throws -> Authentication { let kq = KeychainQuery() @@ -40,22 +42,28 @@ public struct KeychainHelper: Sendable { ) } + /// Delete authorization data for a given domain from the keychain. public func delete(domain: String) throws { let kq = KeychainQuery() try kq.delete(id: self.id, host: domain) } + /// Save authorization data for a given domain to the keychain. public func save(domain: String, username: String, password: String) throws { let kq = KeychainQuery() try kq.save(id: self.id, host: domain, user: username, token: password) } + /// Prompt for authorization data for a given domain to be saved to the keychain. + /// This will cause the current terminal to enter a password prompt state where + /// key strokes are hidden. public func credentialPrompt(domain: String) throws -> Authentication { let username = try userPrompt(domain: domain) let password = try passwordPrompt() return BasicAuthentication(username: username, password: password) } + /// Prompts the current stdin for a username entry and then returns the value. public func userPrompt(domain: String) throws -> String { print("Provide registry username \(domain): ", terminator: "") guard let username = readLine() else { @@ -64,6 +72,9 @@ public struct KeychainHelper: Sendable { return username } + /// Prompts the current stdin for a password entry and then returns the value. + /// This will cause the current stdin (if it is a terminal) to hide keystrokes + /// by disabling echo. public func passwordPrompt() throws -> String { print("Provide registry password: ", terminator: "") let console = try Terminal.current diff --git a/Sources/ContainerizationOCI/Config.swift b/Sources/ContainerizationOCI/ImageConfig.swift similarity index 100% rename from Sources/ContainerizationOCI/Config.swift rename to Sources/ContainerizationOCI/ImageConfig.swift diff --git a/Sources/ContainerizationOS/AsyncSignalHandler.swift b/Sources/ContainerizationOS/AsyncSignalHandler.swift index f699bb69..0b06ef64 100644 --- a/Sources/ContainerizationOS/AsyncSignalHandler.swift +++ b/Sources/ContainerizationOS/AsyncSignalHandler.swift @@ -17,6 +17,8 @@ import Foundation import Synchronization +/// Async friendly wrapper around DispatchSourceSignal. Provides an AsyncStream +/// interface to get notified of received signals. public final class AsyncSignalHandler: Sendable { /// An async stream that returns the signal that was caught, if ever public var signals: AsyncStream { diff --git a/Sources/ContainerizationOS/File.swift b/Sources/ContainerizationOS/File.swift index bf03e831..a9978efd 100644 --- a/Sources/ContainerizationOS/File.swift +++ b/Sources/ContainerizationOS/File.swift @@ -16,6 +16,7 @@ import Foundation +/// Trivial type to discover information about a given file (uid, gid, mode...). public struct File: Sendable { public enum Error: Swift.Error, CustomStringConvertible { case errno(_ e: Int32) diff --git a/Sources/ContainerizationOS/Keychain/KeychainQuery.swift b/Sources/ContainerizationOS/Keychain/KeychainQuery.swift index bb5b0fe5..ec17436f 100644 --- a/Sources/ContainerizationOS/Keychain/KeychainQuery.swift +++ b/Sources/ContainerizationOS/Keychain/KeychainQuery.swift @@ -17,6 +17,7 @@ #if os(macOS) import Foundation +/// Holds the result of a query to the keychain. public struct KeychainQueryResult { public var account: String public var data: String @@ -24,9 +25,11 @@ public struct KeychainQueryResult { public var createdDate: Date } +/// Type that facilitates interacting with the macOS keychain. public struct KeychainQuery { public init() {} + /// Save a value to the keychain. public func save(id: String, host: String, user: String, token: String) throws { if try exists(id: id, host: host) { try delete(id: id, host: host) @@ -48,6 +51,7 @@ public struct KeychainQuery { guard status == errSecSuccess else { throw Self.Error.unhandledError(status: status) } } + /// Delete a value from the keychain. public func delete(id: String, host: String) throws { let query: [String: Any] = [ kSecClass as String: kSecClassInternetPassword, @@ -61,6 +65,7 @@ public struct KeychainQuery { } } + /// Retrieve a value from the keychain. public func get(id: String, host: String) throws -> KeychainQueryResult? { let query: [String: Any] = [ kSecClass as String: kSecClassInternetPassword, @@ -113,6 +118,7 @@ public struct KeychainQuery { return true } + /// Check if a value exists in the keychain. public func exists(id: String, host: String) throws -> Bool { let query: [String: Any] = [ kSecClass as String: kSecClassInternetPassword, diff --git a/Sources/ContainerizationOS/Linux/Epoll.swift b/Sources/ContainerizationOS/Linux/Epoll.swift index 3588b3cc..28766755 100644 --- a/Sources/ContainerizationOS/Linux/Epoll.swift +++ b/Sources/ContainerizationOS/Linux/Epoll.swift @@ -27,7 +27,8 @@ import Glibc import Foundation import Synchronization -/// Register file descriptors to receive events. +/// Register file descriptors to receive events via Linux's +/// epoll syscall surface. public final class Epoll: Sendable { public typealias Mask = Int32 public typealias Handler = (@Sendable (Mask) -> Void) diff --git a/Sources/ContainerizationOS/Mount/Mount.swift b/Sources/ContainerizationOS/Mount/Mount.swift index 5eac6964..dd4e041f 100644 --- a/Sources/ContainerizationOS/Mount/Mount.swift +++ b/Sources/ContainerizationOS/Mount/Mount.swift @@ -26,10 +26,10 @@ private let _mount = Glibc.mount private let _umount = Glibc.umount2 #endif -/// Mount package modeled closely from containerd's: https://github.com/containerd/containerd/tree/main/core/mount -/// Technically, this would be fine in the Linux subdirectory as it's Linux specific for now, but that -/// might not always be the case. +// Mount package modeled closely from containerd's: https://github.com/containerd/containerd/tree/main/core/mount +/// `Mount` models a Linux mount (although potentially could be used on other unix platforms), and +/// provides a simple interface to mount what the type describes. public struct Mount: Sendable { // Type specifies the host-specific of the mount. public var type: String @@ -99,6 +99,7 @@ extension Mount { } } + /// Whether the mount is read only. public var readOnly: Bool { for option in self.options { if option == "ro" { @@ -108,6 +109,23 @@ extension Mount { return false } + /// Mount the mount relative to `root` with the current set of data in the object. + /// Optionally provide `createWithPerms` to set the permissions for the directory that + /// it will be mounted at. + public func mount(root: String, createWithPerms: Int16? = nil) throws { + var rootURL = URL(fileURLWithPath: root) + rootURL = rootURL.resolvingSymlinksInPath() + rootURL = rootURL.appendingPathComponent(self.target) + try self.mountToTarget(target: rootURL.path, createWithPerms: createWithPerms) + } + + /// Mount the mount with the current set of data in the object. Optionally + /// provide `createWithPerms` to set the permissions for the directory that + /// it will be mounted at. + public func mount(createWithPerms: Int16? = nil) throws { + try self.mountToTarget(target: self.target, createWithPerms: createWithPerms) + } + private func mountToTarget(target: String, createWithPerms: Int16?) throws { let pageSize = sysconf(_SC_PAGESIZE) @@ -154,17 +172,6 @@ extension Mount { } } - public func mount(root: String, createWithPerms: Int16? = nil) throws { - var rootURL = URL(fileURLWithPath: root) - rootURL = rootURL.resolvingSymlinksInPath() - rootURL = rootURL.appendingPathComponent(self.target) - try self.mountToTarget(target: rootURL.path, createWithPerms: createWithPerms) - } - - public func mount(createWithPerms: Int16? = nil) throws { - try self.mountToTarget(target: self.target, createWithPerms: createWithPerms) - } - private func mkdirAll(_ name: String, _ perm: Int16) throws { try FileManager.default.createDirectory( atPath: name, diff --git a/Sources/ContainerizationOS/NSLock+Closure.swift b/Sources/ContainerizationOS/NSLock+Closure.swift deleted file mode 100644 index defa5eeb..00000000 --- a/Sources/ContainerizationOS/NSLock+Closure.swift +++ /dev/null @@ -1,27 +0,0 @@ -//===----------------------------------------------------------------------===// -// Copyright © 2025 Apple Inc. and the containerization project authors. All rights reserved. -// -// 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. -//===----------------------------------------------------------------------===// - -import Foundation - -extension NSLock { - /// lock during the execution of the provided function - public func lock(_ fn: () throws -> T) rethrows -> T { - self.lock() - defer { self.unlock() } - - return try fn() - } -} diff --git a/Sources/ContainerizationOS/Path.swift b/Sources/ContainerizationOS/Path.swift index 9ef0fa82..c8502d9f 100644 --- a/Sources/ContainerizationOS/Path.swift +++ b/Sources/ContainerizationOS/Path.swift @@ -16,6 +16,8 @@ import Foundation +/// `Path` provides utilities to look for binaries in the current PATH, +/// or to return the current PATH. public struct Path { /// lookPath looks up an executable's path from $PATH public static func lookPath(_ name: String) -> URL? { diff --git a/Sources/ContainerizationOS/Signals.swift b/Sources/ContainerizationOS/Signals.swift index 7cceb432..33f5542c 100644 --- a/Sources/ContainerizationOS/Signals.swift +++ b/Sources/ContainerizationOS/Signals.swift @@ -16,6 +16,7 @@ import Foundation +/// Helper type with utilities to parse and manipulate unix signals. public struct Signals { public static func allNumeric() -> [Int32] { Array(Signals.all.values) diff --git a/Sources/ContainerizationOS/Socket/Socket.swift b/Sources/ContainerizationOS/Socket/Socket.swift index 9a3458f9..fbff6646 100644 --- a/Sources/ContainerizationOS/Socket/Socket.swift +++ b/Sources/ContainerizationOS/Socket/Socket.swift @@ -44,6 +44,7 @@ let sysConnect = connect let sysIoctl: @convention(c) (CInt, CUnsignedLong, UnsafeMutableRawPointer) -> CInt = ioctl #endif +/// Thread-safe socket wrapper. public final class Socket: Sendable { public enum TimeoutOption { case send diff --git a/Sources/ContainerizationOS/Socket/SocketType.swift b/Sources/ContainerizationOS/Socket/SocketType.swift index 563334e9..62d87374 100644 --- a/Sources/ContainerizationOS/Socket/SocketType.swift +++ b/Sources/ContainerizationOS/Socket/SocketType.swift @@ -24,6 +24,7 @@ import Darwin #error("SocketType not supported on this platform.") #endif +/// Protocol used to describe the family of socket to be created with `Socket`. public protocol SocketType: Sendable, CustomStringConvertible { var domain: Int32 { get } var type: Int32 { get } diff --git a/Sources/ContainerizationOS/Socket/UnixType.swift b/Sources/ContainerizationOS/Socket/UnixType.swift index 913582c5..9bb7ed39 100644 --- a/Sources/ContainerizationOS/Socket/UnixType.swift +++ b/Sources/ContainerizationOS/Socket/UnixType.swift @@ -27,6 +27,7 @@ let _SOCK_STREAM = SOCK_STREAM #error("UnixType not supported on this platform.") #endif +/// Unix domain socket variant of `SocketType`. public struct UnixType: SocketType, Sendable, CustomStringConvertible { public var domain: Int32 { AF_UNIX } public var type: Int32 { _SOCK_STREAM } diff --git a/Sources/ContainerizationOS/Socket/VsockType.swift b/Sources/ContainerizationOS/Socket/VsockType.swift index 17174e70..ce547f8d 100644 --- a/Sources/ContainerizationOS/Socket/VsockType.swift +++ b/Sources/ContainerizationOS/Socket/VsockType.swift @@ -26,6 +26,7 @@ import Darwin #error("VsockType not supported on this platform.") #endif +/// Vsock variant of `SocketType`. public struct VsockType: SocketType, Sendable { public var domain: Int32 { AF_VSOCK } public var type: Int32 { _SOCK_STREAM } diff --git a/Sources/ContainerizationOS/Syscall.swift b/Sources/ContainerizationOS/Syscall.swift index 7eb49622..a4e6b6bf 100644 --- a/Sources/ContainerizationOS/Syscall.swift +++ b/Sources/ContainerizationOS/Syscall.swift @@ -24,6 +24,7 @@ import Darwin #error("retryingSyscall not supported on this platform.") #endif +/// Helper type to deal with running system calls. public struct Syscall { /// Retry a syscall on EINTR. public static func retrying(_ closure: () -> T) -> T { diff --git a/Sources/ContainerizationOS/Sysctl.swift b/Sources/ContainerizationOS/Sysctl.swift index 504b90c7..b1ebec32 100644 --- a/Sources/ContainerizationOS/Sysctl.swift +++ b/Sources/ContainerizationOS/Sysctl.swift @@ -16,6 +16,7 @@ import Foundation +/// Helper type to deal with system control functionalities. public struct Sysctl { #if os(macOS) /// Simple `sysctlbyname` wrapper. diff --git a/Sources/ContainerizationOS/Terminal.swift b/Sources/ContainerizationOS/Terminal.swift index 65125c9d..5ad69b64 100644 --- a/Sources/ContainerizationOS/Terminal.swift +++ b/Sources/ContainerizationOS/Terminal.swift @@ -16,6 +16,8 @@ import Foundation +/// `Terminal` provides a clean interface to deal with terminal +/// interactions on Unix platforms. public struct Terminal: Sendable { private let initState: termios? diff --git a/Sources/ContainerizationOS/User.swift b/Sources/ContainerizationOS/User.swift index ee306b7e..0e15c845 100644 --- a/Sources/ContainerizationOS/User.swift +++ b/Sources/ContainerizationOS/User.swift @@ -17,6 +17,8 @@ import ContainerizationError import Foundation +/// `User` provides utilities to ensure that a given username exists in +/// /etc/passwd (and /etc/group). public enum User { private static let passwdFile = "/etc/passwd" private static let groupFile = "/etc/group" diff --git a/Sources/cctl/ContainerStore.swift b/Sources/cctl/ContainerStore.swift index 902d5805..64a21ec8 100644 --- a/Sources/cctl/ContainerStore.swift +++ b/Sources/cctl/ContainerStore.swift @@ -35,7 +35,7 @@ extension String { #endif -public final class ContainerStore: Sendable { +struct ContainerStore: Sendable { private static let initImage = "vminit:latest" private let content: ContentStore