From 2edb3c19c6a91c77fdf8f1cc0d054e686de420c9 Mon Sep 17 00:00:00 2001 From: Dmitry Kovba Date: Mon, 18 May 2026 11:13:28 -0700 Subject: [PATCH 1/6] Compile the `vminitd` sources only when targeting Linux --- vminitd/Sources/Cgroup/Cgroup2Manager.swift | 4 ++-- vminitd/Sources/LCShim/include/syscall.h | 2 ++ vminitd/Sources/LCShim/syscall.c | 2 ++ vminitd/Sources/VminitdCore/AgentCommand.swift | 4 ++++ vminitd/Sources/VminitdCore/CommandRunner.swift | 4 ++++ vminitd/Sources/VminitdCore/ContainerProcess.swift | 4 ++++ vminitd/Sources/VminitdCore/HostStdio.swift | 4 ++++ vminitd/Sources/VminitdCore/IOCloser+Extensions.swift | 4 ++++ vminitd/Sources/VminitdCore/IOCloser.swift | 4 ++++ vminitd/Sources/VminitdCore/IOPair.swift | 4 ++++ vminitd/Sources/VminitdCore/InitCommand.swift | 4 ++++ vminitd/Sources/VminitdCore/Logging.swift | 4 ++++ vminitd/Sources/VminitdCore/ManagedContainer.swift | 4 ++++ vminitd/Sources/VminitdCore/ManagedProcess.swift | 4 ++++ vminitd/Sources/VminitdCore/OSFile+Splice.swift | 4 ++++ vminitd/Sources/VminitdCore/OSFile.swift | 4 ++++ vminitd/Sources/VminitdCore/PauseCommand.swift | 4 ++++ vminitd/Sources/VminitdCore/ProcessSupervisor.swift | 4 ++++ vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift | 6 +++--- vminitd/Sources/VminitdCore/Runc/Runc.swift | 4 ++++ vminitd/Sources/VminitdCore/RuncProcess.swift | 2 +- vminitd/Sources/VminitdCore/Server+GRPC.swift | 4 ++++ vminitd/Sources/VminitdCore/Server.swift | 4 ++++ vminitd/Sources/VminitdCore/StandardIO.swift | 4 ++++ vminitd/Sources/VminitdCore/TerminalIO.swift | 4 ++++ vminitd/Sources/VminitdCore/VsockProxy.swift | 4 ++++ 26 files changed, 94 insertions(+), 6 deletions(-) diff --git a/vminitd/Sources/Cgroup/Cgroup2Manager.swift b/vminitd/Sources/Cgroup/Cgroup2Manager.swift index da6b72f6..a8146b5b 100644 --- a/vminitd/Sources/Cgroup/Cgroup2Manager.swift +++ b/vminitd/Sources/Cgroup/Cgroup2Manager.swift @@ -14,12 +14,12 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + // NOTE: Ideally this should live in ContainerizationOS/Linux, or just ContainerizationCgroups // or something similar, but it's not there yet. It does what we need, but it'd need a lot more // features and testing before it's ready to be public. -#if os(Linux) - #if canImport(Musl) import Musl #elseif canImport(Glibc) diff --git a/vminitd/Sources/LCShim/include/syscall.h b/vminitd/Sources/LCShim/include/syscall.h index dbbc8813..815dd476 100644 --- a/vminitd/Sources/LCShim/include/syscall.h +++ b/vminitd/Sources/LCShim/include/syscall.h @@ -22,7 +22,9 @@ #define __SYSCALL_H #include +#ifdef __linux__ #include +#endif // CLONE_* flags #ifndef CLONE_NEWNS diff --git a/vminitd/Sources/LCShim/syscall.c b/vminitd/Sources/LCShim/syscall.c index f65c75aa..094f6c61 100644 --- a/vminitd/Sources/LCShim/syscall.c +++ b/vminitd/Sources/LCShim/syscall.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#ifdef __linux__ #include #include #include @@ -48,3 +49,4 @@ int CZ_setrlimit(int resource, unsigned long long soft, limit.rlim_max = (rlim_t)hard; return setrlimit(resource, &limit); } +#endif diff --git a/vminitd/Sources/VminitdCore/AgentCommand.swift b/vminitd/Sources/VminitdCore/AgentCommand.swift index bc002169..9068ecd7 100644 --- a/vminitd/Sources/VminitdCore/AgentCommand.swift +++ b/vminitd/Sources/VminitdCore/AgentCommand.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ArgumentParser import CVersion import Cgroup @@ -214,3 +216,5 @@ public struct AgentCommand: AsyncParsableCommand { } } } + +#endif diff --git a/vminitd/Sources/VminitdCore/CommandRunner.swift b/vminitd/Sources/VminitdCore/CommandRunner.swift index 3ca0f50e..6387d7e1 100644 --- a/vminitd/Sources/VminitdCore/CommandRunner.swift +++ b/vminitd/Sources/VminitdCore/CommandRunner.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationOS import Foundation import Synchronization @@ -102,3 +104,5 @@ final class ReaperCommandRunner: CommandRunner, Sendable { } } } + +#endif diff --git a/vminitd/Sources/VminitdCore/ContainerProcess.swift b/vminitd/Sources/VminitdCore/ContainerProcess.swift index 8f28ec8d..e9fe82be 100644 --- a/vminitd/Sources/VminitdCore/ContainerProcess.swift +++ b/vminitd/Sources/VminitdCore/ContainerProcess.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationOS import Foundation @@ -64,3 +66,5 @@ protocol ContainerProcess: Sendable { /// Set the exit status of the process. func setExit(_ status: Int32) } + +#endif diff --git a/vminitd/Sources/VminitdCore/HostStdio.swift b/vminitd/Sources/VminitdCore/HostStdio.swift index 3c1d0a33..2bb31392 100644 --- a/vminitd/Sources/VminitdCore/HostStdio.swift +++ b/vminitd/Sources/VminitdCore/HostStdio.swift @@ -14,9 +14,13 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + struct HostStdio: Sendable { let stdin: UInt32? let stdout: UInt32? let stderr: UInt32? let terminal: Bool } + +#endif diff --git a/vminitd/Sources/VminitdCore/IOCloser+Extensions.swift b/vminitd/Sources/VminitdCore/IOCloser+Extensions.swift index 38e848b6..9a441084 100644 --- a/vminitd/Sources/VminitdCore/IOCloser+Extensions.swift +++ b/vminitd/Sources/VminitdCore/IOCloser+Extensions.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationOS import Foundation @@ -26,3 +28,5 @@ extension Terminal: IOCloser { } extension FileHandle: IOCloser {} + +#endif diff --git a/vminitd/Sources/VminitdCore/IOCloser.swift b/vminitd/Sources/VminitdCore/IOCloser.swift index 33514863..eb27ff2c 100644 --- a/vminitd/Sources/VminitdCore/IOCloser.swift +++ b/vminitd/Sources/VminitdCore/IOCloser.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + protocol IOCloser: Sendable { var fileDescriptor: Int32 { get } @@ -31,3 +33,5 @@ struct UnownedIOCloser: IOCloser { func close() throws {} } + +#endif diff --git a/vminitd/Sources/VminitdCore/IOPair.swift b/vminitd/Sources/VminitdCore/IOPair.swift index d9ef1529..7d9df425 100644 --- a/vminitd/Sources/VminitdCore/IOPair.swift +++ b/vminitd/Sources/VminitdCore/IOPair.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationError import ContainerizationOS import Foundation @@ -183,3 +185,5 @@ final class IOPair: Sendable { } } } + +#endif diff --git a/vminitd/Sources/VminitdCore/InitCommand.swift b/vminitd/Sources/VminitdCore/InitCommand.swift index 35239856..ea15dd3a 100644 --- a/vminitd/Sources/VminitdCore/InitCommand.swift +++ b/vminitd/Sources/VminitdCore/InitCommand.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ArgumentParser import ContainerizationOS import LCShim @@ -107,3 +109,5 @@ public struct InitCommand: ParsableCommand { _exit(childExitStatus ?? 1) } } + +#endif diff --git a/vminitd/Sources/VminitdCore/Logging.swift b/vminitd/Sources/VminitdCore/Logging.swift index c386b7b3..d96d74d6 100644 --- a/vminitd/Sources/VminitdCore/Logging.swift +++ b/vminitd/Sources/VminitdCore/Logging.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ArgumentParser import Foundation import Logging @@ -97,3 +99,5 @@ private struct StderrLogHandler: LogHandler { return String(format: "%@.%03dZ", buf, ms) } } + +#endif diff --git a/vminitd/Sources/VminitdCore/ManagedContainer.swift b/vminitd/Sources/VminitdCore/ManagedContainer.swift index ff8eeea9..9efa5c40 100644 --- a/vminitd/Sources/VminitdCore/ManagedContainer.swift +++ b/vminitd/Sources/VminitdCore/ManagedContainer.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import Cgroup import ContainerizationError import ContainerizationOCI @@ -280,3 +282,5 @@ extension ManagedContainer { URL(fileURLWithPath: "/run/container").appending(path: id) } } + +#endif diff --git a/vminitd/Sources/VminitdCore/ManagedProcess.swift b/vminitd/Sources/VminitdCore/ManagedProcess.swift index 645240ba..ba4cd2d1 100644 --- a/vminitd/Sources/VminitdCore/ManagedProcess.swift +++ b/vminitd/Sources/VminitdCore/ManagedProcess.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import Cgroup import Containerization import ContainerizationError @@ -342,3 +344,5 @@ extension ManagedProcess { // when it exits and IO is closed via setExit() } } + +#endif diff --git a/vminitd/Sources/VminitdCore/OSFile+Splice.swift b/vminitd/Sources/VminitdCore/OSFile+Splice.swift index 45c99248..7101b43b 100644 --- a/vminitd/Sources/VminitdCore/OSFile+Splice.swift +++ b/vminitd/Sources/VminitdCore/OSFile+Splice.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import Foundation import LCShim @@ -100,3 +102,5 @@ extension OSFile { } } } + +#endif diff --git a/vminitd/Sources/VminitdCore/OSFile.swift b/vminitd/Sources/VminitdCore/OSFile.swift index bafef15b..05f28fb7 100644 --- a/vminitd/Sources/VminitdCore/OSFile.swift +++ b/vminitd/Sources/VminitdCore/OSFile.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import Foundation struct OSFile: Sendable { @@ -126,3 +128,5 @@ struct OSFile: Sendable { return Self(fd: fd) } } + +#endif diff --git a/vminitd/Sources/VminitdCore/PauseCommand.swift b/vminitd/Sources/VminitdCore/PauseCommand.swift index 649e1d58..10eaa087 100644 --- a/vminitd/Sources/VminitdCore/PauseCommand.swift +++ b/vminitd/Sources/VminitdCore/PauseCommand.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ArgumentParser import Dispatch import Logging @@ -76,3 +78,5 @@ public struct PauseCommand: ParsableCommand { _exit(42) } } + +#endif diff --git a/vminitd/Sources/VminitdCore/ProcessSupervisor.swift b/vminitd/Sources/VminitdCore/ProcessSupervisor.swift index 1af16256..82e767b2 100644 --- a/vminitd/Sources/VminitdCore/ProcessSupervisor.swift +++ b/vminitd/Sources/VminitdCore/ProcessSupervisor.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationOS import Foundation import Logging @@ -162,3 +164,5 @@ final class ProcessSupervisor: Sendable { poller.shutdown() } } + +#endif diff --git a/vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift b/vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift index 46678304..6e31b89f 100644 --- a/vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift +++ b/vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift @@ -14,11 +14,11 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationOS import Foundation -#if os(Linux) - /// A Unix socket for receiving PTY master file descriptors from runc public final class ConsoleSocket: Sendable { private let socket: Socket @@ -80,4 +80,4 @@ public final class ConsoleSocket: Sendable { } } -#endif // os(Linux) +#endif diff --git a/vminitd/Sources/VminitdCore/Runc/Runc.swift b/vminitd/Sources/VminitdCore/Runc/Runc.swift index b7dc3a09..9c5447b2 100644 --- a/vminitd/Sources/VminitdCore/Runc/Runc.swift +++ b/vminitd/Sources/VminitdCore/Runc/Runc.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationOCI import ContainerizationOS import Foundation @@ -788,3 +790,5 @@ extension Runc { let limit: UInt64? } } + +#endif diff --git a/vminitd/Sources/VminitdCore/RuncProcess.swift b/vminitd/Sources/VminitdCore/RuncProcess.swift index 77450a59..eae35f81 100644 --- a/vminitd/Sources/VminitdCore/RuncProcess.swift +++ b/vminitd/Sources/VminitdCore/RuncProcess.swift @@ -561,4 +561,4 @@ final class RuncStandardIO: RuncProcess.IO & Sendable { } } -#endif // os(Linux) +#endif diff --git a/vminitd/Sources/VminitdCore/Server+GRPC.swift b/vminitd/Sources/VminitdCore/Server+GRPC.swift index 23cca9f8..8616511f 100644 --- a/vminitd/Sources/VminitdCore/Server+GRPC.swift +++ b/vminitd/Sources/VminitdCore/Server+GRPC.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import Cgroup import Containerization import ContainerizationArchive @@ -1652,3 +1654,5 @@ extension Initd { ociSpec.process = process } } + +#endif diff --git a/vminitd/Sources/VminitdCore/Server.swift b/vminitd/Sources/VminitdCore/Server.swift index c2a841ae..d6a6c030 100644 --- a/vminitd/Sources/VminitdCore/Server.swift +++ b/vminitd/Sources/VminitdCore/Server.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationError import Foundation import GRPCCore @@ -139,3 +141,5 @@ public final class Initd: Sendable { } } } + +#endif diff --git a/vminitd/Sources/VminitdCore/StandardIO.swift b/vminitd/Sources/VminitdCore/StandardIO.swift index 006d25f0..49d32c40 100644 --- a/vminitd/Sources/VminitdCore/StandardIO.swift +++ b/vminitd/Sources/VminitdCore/StandardIO.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationError import ContainerizationOS import Foundation @@ -169,3 +171,5 @@ final class StandardIO: ManagedProcess.IO & Sendable { } } } + +#endif diff --git a/vminitd/Sources/VminitdCore/TerminalIO.swift b/vminitd/Sources/VminitdCore/TerminalIO.swift index d5ebc863..976e9700 100644 --- a/vminitd/Sources/VminitdCore/TerminalIO.swift +++ b/vminitd/Sources/VminitdCore/TerminalIO.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationOS import Foundation import LCShim @@ -163,3 +165,5 @@ final class TerminalIO: ManagedProcess.IO & Sendable { } } } + +#endif diff --git a/vminitd/Sources/VminitdCore/VsockProxy.swift b/vminitd/Sources/VminitdCore/VsockProxy.swift index 0aefb055..e18ebc76 100644 --- a/vminitd/Sources/VminitdCore/VsockProxy.swift +++ b/vminitd/Sources/VminitdCore/VsockProxy.swift @@ -14,6 +14,8 @@ // limitations under the License. //===----------------------------------------------------------------------===// +#if os(Linux) + import ContainerizationIO import ContainerizationOS import Foundation @@ -400,3 +402,5 @@ extension VsockProxy { } } } + +#endif From f82a5c5b7f9ae40e0e80f8a15e9edde7a1b6ae55 Mon Sep 17 00:00:00 2001 From: Dmitry Kovba Date: Mon, 18 May 2026 14:48:44 -0700 Subject: [PATCH 2/6] Move the `VminitdCore` library to the top-level package --- Package.swift | 49 ++++++++++++++++++ vminitd/Package.swift | 55 +-------------------- vminitd/Sources/Cgroup/Cgroup2Manager.swift | 10 ++-- 3 files changed, 56 insertions(+), 58 deletions(-) diff --git a/Package.swift b/Package.swift index 37a899ef..720fc37c 100644 --- a/Package.swift +++ b/Package.swift @@ -21,6 +21,10 @@ import CompilerPluginSupport import Foundation import PackageDescription +let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified" +let gitTag = ProcessInfo.processInfo.environment["GIT_TAG"] ?? "" +let buildTime = ProcessInfo.processInfo.environment["BUILD_TIME"] ?? "unspecified" + let package = Package( name: "containerization", platforms: [.macOS("15.0")], @@ -33,6 +37,7 @@ let package = Package( .library(name: "ContainerizationOS", targets: ["ContainerizationOS"]), .library(name: "ContainerizationExtras", targets: ["ContainerizationExtras"]), .library(name: "ContainerizationArchive", targets: ["ContainerizationArchive"]), + .library(name: "VminitdCore", targets: ["VminitdCore", "Cgroup", "LCShim"]), .executable(name: "cctl", targets: ["cctl"]), ], dependencies: [ @@ -259,6 +264,50 @@ let package = Package( .target( name: "CShim" ), + .target( + name: "CVersion", + path: "vminitd/Sources/CVersion", + cSettings: [ + .define("GIT_COMMIT", to: "\"\(gitCommit)\""), + .define("GIT_TAG", to: "\"\(gitTag)\""), + .define("BUILD_TIME", to: "\"\(buildTime)\""), + ] + ), + .target( + name: "LCShim", + path: "vminitd/Sources/LCShim" + ), + .target( + name: "Cgroup", + dependencies: [ + .product(name: "Logging", package: "swift-log"), + "ContainerizationOCI", + "ContainerizationOS", + .product(name: "SystemPackage", package: "swift-system"), + "LCShim", + ], + path: "vminitd/Sources/Cgroup" + ), + .target( + name: "VminitdCore", + dependencies: [ + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "Logging", package: "swift-log"), + "Containerization", + "ContainerizationArchive", + "ContainerizationNetlink", + "ContainerizationIO", + "ContainerizationOS", + .product(name: "SystemPackage", package: "swift-system"), + .product(name: "GRPCCore", package: "grpc-swift-2"), + .product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"), + .product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"), + "LCShim", + "CVersion", + "Cgroup", + ], + path: "vminitd/Sources/VminitdCore" + ), ] ) diff --git a/vminitd/Package.swift b/vminitd/Package.swift index 03f1232a..e97b55f7 100644 --- a/vminitd/Package.swift +++ b/vminitd/Package.swift @@ -17,79 +17,29 @@ // The swift-tools-version declares the minimum version of Swift required to build this package. -import Foundation import PackageDescription -let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified" -let gitTag = ProcessInfo.processInfo.environment["GIT_TAG"] ?? "" -let buildTime = ProcessInfo.processInfo.environment["BUILD_TIME"] ?? "unspecified" - let package = Package( name: "swift-vminitd", platforms: [.macOS("15")], products: [ - .library(name: "VminitdCore", targets: ["VminitdCore"]), .executable(name: "vminitd", targets: ["vminitd"]), .executable(name: "vmexec", targets: ["vmexec"]), ], dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.7.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.10.1"), - .package(url: "https://github.com/apple/swift-protobuf.git", from: "1.36.0"), .package(url: "https://github.com/apple/swift-system.git", from: "1.6.4"), - .package(url: "https://github.com/grpc/grpc-swift-2.git", from: "2.3.0"), - .package(url: "https://github.com/grpc/grpc-swift-nio-transport.git", from: "2.4.4"), - .package(url: "https://github.com/grpc/grpc-swift-protobuf.git", from: "2.2.0"), .package(name: "containerization", path: "../"), ], targets: [ - .target( - name: "CVersion", - cSettings: [ - .define("GIT_COMMIT", to: "\"\(gitCommit)\""), - .define("GIT_TAG", to: "\"\(gitTag)\""), - .define("BUILD_TIME", to: "\"\(buildTime)\""), - ] - ), - .target( - name: "LCShim" - ), - .target( - name: "Cgroup", - dependencies: [ - .product(name: "Logging", package: "swift-log"), - .product(name: "ContainerizationOCI", package: "containerization"), - .product(name: "ContainerizationOS", package: "containerization"), - .product(name: "SystemPackage", package: "swift-system"), - "LCShim", - ] - ), - .target( - name: "VminitdCore", - dependencies: [ - .product(name: "ArgumentParser", package: "swift-argument-parser"), - .product(name: "Logging", package: "swift-log"), - .product(name: "Containerization", package: "containerization"), - .product(name: "ContainerizationArchive", package: "containerization"), - .product(name: "ContainerizationNetlink", package: "containerization"), - .product(name: "ContainerizationIO", package: "containerization"), - .product(name: "ContainerizationOS", package: "containerization"), - .product(name: "SystemPackage", package: "swift-system"), - .product(name: "GRPCCore", package: "grpc-swift-2"), - .product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"), - .product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"), - "LCShim", - "CVersion", - "Cgroup", - ] - ), .executableTarget( name: "vminitd", dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "ContainerizationOS", package: "containerization"), .product(name: "Logging", package: "swift-log"), - "VminitdCore", + .product(name: "VminitdCore", package: "containerization"), ] ), .executableTarget( @@ -100,8 +50,7 @@ let package = Package( .product(name: "SystemPackage", package: "swift-system"), .product(name: "Containerization", package: "containerization"), .product(name: "ContainerizationOS", package: "containerization"), - "LCShim", - "Cgroup", + .product(name: "VminitdCore", package: "containerization"), ] ), ] diff --git a/vminitd/Sources/Cgroup/Cgroup2Manager.swift b/vminitd/Sources/Cgroup/Cgroup2Manager.swift index a8146b5b..bd5f6b46 100644 --- a/vminitd/Sources/Cgroup/Cgroup2Manager.swift +++ b/vminitd/Sources/Cgroup/Cgroup2Manager.swift @@ -43,8 +43,8 @@ package enum Cgroup2Controller: String { // Extremely simple cgroup manager. Our needs are simple for now, and this is // reflected in the type. -package struct Cgroup2Manager: Sendable { - package static let defaultMountPoint = URL(filePath: "/sys/fs/cgroup") +public struct Cgroup2Manager: Sendable { + public static let defaultMountPoint = URL(filePath: "/sys/fs/cgroup") private static let killFile = "cgroup.kill" private static let procsFile = "cgroup.procs" @@ -66,7 +66,7 @@ package struct Cgroup2Manager: Sendable { self.logger = logger } - package static func load( + public static func load( mountPoint: URL = Self.defaultMountPoint, group: URL, logger: Logger? = nil @@ -183,7 +183,7 @@ package struct Cgroup2Manager: Sendable { } } - package func addProcess(pid: Int32) throws { + public func addProcess(pid: Int32) throws { self.logger?.debug( "adding new proc to cgroup", metadata: [ @@ -199,7 +199,7 @@ package struct Cgroup2Manager: Sendable { ) } - package func applyResources(resources: ContainerizationOCI.LinuxResources) throws { + public func applyResources(resources: ContainerizationOCI.LinuxResources) throws { self.logger?.debug( "applying cgroup resources", metadata: [ From 77c75a5bf6a5974047cea20471677c10052e0e70 Mon Sep 17 00:00:00 2001 From: Dmitry Kovba Date: Tue, 19 May 2026 15:12:52 -0700 Subject: [PATCH 3/6] Trim the `VminitdCore` public API surface --- vminitd/Sources/VminitdCore/AgentCommand.swift | 2 +- vminitd/Sources/VminitdCore/InitCommand.swift | 6 +++--- vminitd/Sources/VminitdCore/Logging.swift | 4 ++-- vminitd/Sources/VminitdCore/PauseCommand.swift | 2 +- vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift | 12 ++++++------ vminitd/Sources/VminitdCore/Server.swift | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/vminitd/Sources/VminitdCore/AgentCommand.swift b/vminitd/Sources/VminitdCore/AgentCommand.swift index 9068ecd7..234ae56b 100644 --- a/vminitd/Sources/VminitdCore/AgentCommand.swift +++ b/vminitd/Sources/VminitdCore/AgentCommand.swift @@ -46,7 +46,7 @@ public struct AgentCommand: AsyncParsableCommand { private static let foregroundEnvVar = "FOREGROUND" public static let vsockPort = 1024 - @OptionGroup public var options: LogLevelOption + @OptionGroup var options: LogLevelOption public init() {} diff --git a/vminitd/Sources/VminitdCore/InitCommand.swift b/vminitd/Sources/VminitdCore/InitCommand.swift index ea15dd3a..1357b8eb 100644 --- a/vminitd/Sources/VminitdCore/InitCommand.swift +++ b/vminitd/Sources/VminitdCore/InitCommand.swift @@ -44,13 +44,13 @@ public struct InitCommand: ParsableCommand { public init() {} @Flag(name: .shortAndLong, help: "Send signals to the child's process group instead of just the child") - public var processGroup: Bool = false + var processGroup: Bool = false @Argument(help: "The command to run") - public var command: String + var command: String @Argument(parsing: .captureForPassthrough, help: "Arguments for the command") - public var arguments: [String] = [] + var arguments: [String] = [] /// Signals that should NOT be forwarded to the child. private static let ignoredSignals: Set = [ diff --git a/vminitd/Sources/VminitdCore/Logging.swift b/vminitd/Sources/VminitdCore/Logging.swift index d96d74d6..7a634909 100644 --- a/vminitd/Sources/VminitdCore/Logging.swift +++ b/vminitd/Sources/VminitdCore/Logging.swift @@ -23,7 +23,7 @@ import Synchronization public struct LogLevelOption: ParsableArguments { @Option(name: .long, help: "Set the log level (trace, debug, info, notice, warning, error, critical)") - public var logLevel: String = "info" + var logLevel: String = "info" public init() {} @@ -51,7 +51,7 @@ public struct LogLevelOption: ParsableArguments { private let _loggingBootstrapped = Mutex(false) -public func makeLogger(label: String, level: Logger.Level) -> Logger { +func makeLogger(label: String, level: Logger.Level) -> Logger { _loggingBootstrapped.withLock { bootstrapped in if !bootstrapped { LoggingSystem.bootstrap { label in StderrLogHandler(label: label) } diff --git a/vminitd/Sources/VminitdCore/PauseCommand.swift b/vminitd/Sources/VminitdCore/PauseCommand.swift index 10eaa087..968f4071 100644 --- a/vminitd/Sources/VminitdCore/PauseCommand.swift +++ b/vminitd/Sources/VminitdCore/PauseCommand.swift @@ -36,7 +36,7 @@ public struct PauseCommand: ParsableCommand { public init() {} - @OptionGroup public var options: LogLevelOption + @OptionGroup var options: LogLevelOption public mutating func run() throws { let log = makeLogger(label: "pause", level: options.resolvedLogLevel()) diff --git a/vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift b/vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift index 6e31b89f..48d4a02e 100644 --- a/vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift +++ b/vminitd/Sources/VminitdCore/Runc/ConsoleSocket.swift @@ -20,15 +20,15 @@ import ContainerizationOS import Foundation /// A Unix socket for receiving PTY master file descriptors from runc -public final class ConsoleSocket: Sendable { +final class ConsoleSocket: Sendable { private let socket: Socket private let socketPath: String /// The path to the console socket - public var path: String { socketPath } + var path: String { socketPath } /// Create a new console socket at the specified path - public init(path: String) throws { + init(path: String) throws { let absPath = path.starts(with: "/") ? path : FileManager.default.currentDirectoryPath + "/" + path self.socketPath = absPath @@ -47,7 +47,7 @@ public final class ConsoleSocket: Sendable { } /// Create a temporary console socket in the runtime directory - public static func temporary() throws -> ConsoleSocket { + static func temporary() throws -> ConsoleSocket { let tmpDir = "/tmp" let socketDir = tmpDir + "/runc-console-\(UUID().uuidString)" let socketPath = socketDir + "/console.sock" @@ -63,14 +63,14 @@ public final class ConsoleSocket: Sendable { } /// Receive the PTY master file descriptor from runc - public func receiveMaster() throws -> Int32 { + func receiveMaster() throws -> Int32 { let connection = try socket.accept() defer { try? connection.close() } return try connection.receiveFileDescriptor() } /// Close the socket and optionally remove the socket file - public func close() throws { + func close() throws { try socket.close() try FileManager.default.removeItem(atPath: socketPath) } diff --git a/vminitd/Sources/VminitdCore/Server.swift b/vminitd/Sources/VminitdCore/Server.swift index d6a6c030..7ee16744 100644 --- a/vminitd/Sources/VminitdCore/Server.swift +++ b/vminitd/Sources/VminitdCore/Server.swift @@ -27,7 +27,7 @@ import NIOPosix public final class Initd: Sendable { public actor State { - public private(set) var containers: [String: ManagedContainer] = [:] + private(set) var containers: [String: ManagedContainer] = [:] var proxies: [String: VsockProxy] = [:] public typealias ContainerDeletedHandler = @Sendable (String) async -> Void From 325939767eafb4f6b6e1069852645019d80bd30e Mon Sep 17 00:00:00 2001 From: Dmitry Kovba Date: Tue, 19 May 2026 15:51:54 -0700 Subject: [PATCH 4/6] Remove the duplicate `.swiftpm` entry from `.gitignore` --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 002f6c18..63669d43 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ xcuserdata/ DerivedData/ .swiftpm/ .netrc -.swiftpm workdir/ installer/ .venv/ From ad54d20907a819ca1a82809076df5b545f3d52f3 Mon Sep 17 00:00:00 2001 From: Dmitry Kovba Date: Wed, 20 May 2026 11:58:47 -0700 Subject: [PATCH 5/6] Move the container `cgroup` setup from `vmexec` to `vminitd` --- Package.swift | 2 +- vminitd/Sources/Cgroup/Cgroup2Manager.swift | 30 ++++--------------- .../VminitdCore/ManagedContainer.swift | 3 ++ .../Sources/VminitdCore/ManagedProcess.swift | 16 ++++++---- vminitd/Sources/vmexec/RunCommand.swift | 15 ---------- 5 files changed, 20 insertions(+), 46 deletions(-) diff --git a/Package.swift b/Package.swift index 720fc37c..b2198f85 100644 --- a/Package.swift +++ b/Package.swift @@ -37,7 +37,7 @@ let package = Package( .library(name: "ContainerizationOS", targets: ["ContainerizationOS"]), .library(name: "ContainerizationExtras", targets: ["ContainerizationExtras"]), .library(name: "ContainerizationArchive", targets: ["ContainerizationArchive"]), - .library(name: "VminitdCore", targets: ["VminitdCore", "Cgroup", "LCShim"]), + .library(name: "VminitdCore", targets: ["VminitdCore", "LCShim"]), .executable(name: "cctl", targets: ["cctl"]), ], dependencies: [ diff --git a/vminitd/Sources/Cgroup/Cgroup2Manager.swift b/vminitd/Sources/Cgroup/Cgroup2Manager.swift index bd5f6b46..61ca9855 100644 --- a/vminitd/Sources/Cgroup/Cgroup2Manager.swift +++ b/vminitd/Sources/Cgroup/Cgroup2Manager.swift @@ -43,8 +43,8 @@ package enum Cgroup2Controller: String { // Extremely simple cgroup manager. Our needs are simple for now, and this is // reflected in the type. -public struct Cgroup2Manager: Sendable { - public static let defaultMountPoint = URL(filePath: "/sys/fs/cgroup") +package struct Cgroup2Manager: Sendable { + package static let defaultMountPoint = URL(filePath: "/sys/fs/cgroup") private static let killFile = "cgroup.kill" private static let procsFile = "cgroup.procs" @@ -66,7 +66,7 @@ public struct Cgroup2Manager: Sendable { self.logger = logger } - public static func load( + package static func load( mountPoint: URL = Self.defaultMountPoint, group: URL, logger: Logger? = nil @@ -87,26 +87,6 @@ public struct Cgroup2Manager: Sendable { ) } - package static func loadFromPid(pid: Int32, logger: Logger? = nil) throws -> Cgroup2Manager { - let procCgPath = URL(filePath: "/proc/\(pid)/cgroup") - let fh = try FileHandle(forReadingFrom: procCgPath) - guard let data = try fh.readToEnd() else { - throw Error.errno(errno: errno, message: "failed to read \(procCgPath)") - } - - // If this fails we have bigger problems. - let str = String(data: data, encoding: .utf8)! - let parts = str.split(separator: ":") - if parts[0] != "0" { - throw Error.cgroup1 - } - - // We should really read /proc/pid/mountinfo, but for now just assume - // it's always at /sys/fs/cgroup. - let path = parts[1].trimmingCharacters(in: .whitespacesAndNewlines) - return Cgroup2Manager(group: URL(filePath: String(path)), logger: logger) - } - package func create(perms: Int16 = 0o755) throws { self.logger?.info( "creating cgroup manager", @@ -183,7 +163,7 @@ public struct Cgroup2Manager: Sendable { } } - public func addProcess(pid: Int32) throws { + package func addProcess(pid: Int32) throws { self.logger?.debug( "adding new proc to cgroup", metadata: [ @@ -199,7 +179,7 @@ public struct Cgroup2Manager: Sendable { ) } - public func applyResources(resources: ContainerizationOCI.LinuxResources) throws { + package func applyResources(resources: ContainerizationOCI.LinuxResources) throws { self.logger?.debug( "applying cgroup resources", metadata: [ diff --git a/vminitd/Sources/VminitdCore/ManagedContainer.swift b/vminitd/Sources/VminitdCore/ManagedContainer.swift index 9efa5c40..4cb8d82e 100644 --- a/vminitd/Sources/VminitdCore/ManagedContainer.swift +++ b/vminitd/Sources/VminitdCore/ManagedContainer.swift @@ -91,6 +91,8 @@ public actor ManagedContainer { id: id, stdio: stdio, bundle: bundle, + cgroupManager: cgManager, + resources: spec.linux?.resources, owningPid: nil, log: log ) @@ -176,6 +178,7 @@ extension ManagedContainer { id: id, stdio: stdio, bundle: self.bundle, + cgroupManager: self.cgroupManager, owningPid: self.initProcess.pid, log: self.log ) diff --git a/vminitd/Sources/VminitdCore/ManagedProcess.swift b/vminitd/Sources/VminitdCore/ManagedProcess.swift index ba4cd2d1..4888e1aa 100644 --- a/vminitd/Sources/VminitdCore/ManagedProcess.swift +++ b/vminitd/Sources/VminitdCore/ManagedProcess.swift @@ -57,6 +57,8 @@ final class ManagedProcess: ContainerProcess, Sendable { private let command: Command private let state: Mutex private let owningPid: Int32? + private let cgroupManager: Cgroup2Manager? + private let resources: ContainerizationOCI.LinuxResources? private let ackPipe: Pipe private let syncPipe: Pipe private let errorPipe: Pipe @@ -73,6 +75,8 @@ final class ManagedProcess: ContainerProcess, Sendable { id: String, stdio: HostStdio, bundle: ContainerizationOCI.Bundle, + cgroupManager: Cgroup2Manager? = nil, + resources: ContainerizationOCI.LinuxResources? = nil, owningPid: Int32? = nil, log: Logger ) throws { @@ -81,6 +85,8 @@ final class ManagedProcess: ContainerProcess, Sendable { log[metadataKey: "id"] = "\(id)" self.log = log self.owningPid = owningPid + self.cgroupManager = cgroupManager + self.resources = resources let syncPipe = Pipe() try syncPipe.setCloexec() @@ -193,11 +199,11 @@ extension ManagedProcess { ]) $0.pid = pid - // This should probably happen in vmexec, but we don't need to set any cgroup - // toggles so the problem is much simpler to just do it here. - if let owningPid { - let cgManager = try Cgroup2Manager.loadFromPid(pid: owningPid) - try cgManager.addProcess(pid: pid) + if let cgroupManager { + if let resources { + try cgroupManager.applyResources(resources: resources) + } + try cgroupManager.addProcess(pid: pid) } log.info( diff --git a/vminitd/Sources/vmexec/RunCommand.swift b/vminitd/Sources/vmexec/RunCommand.swift index 4c9607fb..20ffb9a5 100644 --- a/vminitd/Sources/vmexec/RunCommand.swift +++ b/vminitd/Sources/vmexec/RunCommand.swift @@ -15,7 +15,6 @@ //===----------------------------------------------------------------------===// import ArgumentParser -import Cgroup import ContainerizationOCI import ContainerizationOS import FoundationEssentials @@ -268,20 +267,6 @@ struct RunCommand: ParsableCommand { if processID == 0 { // child try childSetup(spec: spec, ackPipe: ackPipe, syncPipe: syncPipe) } else { // parent process - // Setup cgroup before child enters cgroup namespace - if let linux = spec.linux { - let cgroupPath = linux.cgroupsPath - if !cgroupPath.isEmpty { - let cgroupManager = try Cgroup2Manager.load(group: URL(filePath: cgroupPath)) - - if let resources = linux.resources { - try cgroupManager.applyResources(resources: resources) - } - - try cgroupManager.addProcess(pid: processID) - } - } - // Send our child's pid before we exit. var childPid = processID try withUnsafeBytes(of: &childPid) { bytes in From 9106691d75c70d786d7264aa2a9ba47963a7460a Mon Sep 17 00:00:00 2001 From: Dmitry Kovba Date: Wed, 20 May 2026 14:11:01 -0700 Subject: [PATCH 6/6] Revert "Move the container `cgroup` setup from `vmexec` to `vminitd`" This reverts commit ad54d20907a819ca1a82809076df5b545f3d52f3. --- Package.swift | 2 +- vminitd/Sources/Cgroup/Cgroup2Manager.swift | 30 +++++++++++++++---- .../VminitdCore/ManagedContainer.swift | 3 -- .../Sources/VminitdCore/ManagedProcess.swift | 16 ++++------ vminitd/Sources/vmexec/RunCommand.swift | 15 ++++++++++ 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/Package.swift b/Package.swift index b2198f85..720fc37c 100644 --- a/Package.swift +++ b/Package.swift @@ -37,7 +37,7 @@ let package = Package( .library(name: "ContainerizationOS", targets: ["ContainerizationOS"]), .library(name: "ContainerizationExtras", targets: ["ContainerizationExtras"]), .library(name: "ContainerizationArchive", targets: ["ContainerizationArchive"]), - .library(name: "VminitdCore", targets: ["VminitdCore", "LCShim"]), + .library(name: "VminitdCore", targets: ["VminitdCore", "Cgroup", "LCShim"]), .executable(name: "cctl", targets: ["cctl"]), ], dependencies: [ diff --git a/vminitd/Sources/Cgroup/Cgroup2Manager.swift b/vminitd/Sources/Cgroup/Cgroup2Manager.swift index 61ca9855..bd5f6b46 100644 --- a/vminitd/Sources/Cgroup/Cgroup2Manager.swift +++ b/vminitd/Sources/Cgroup/Cgroup2Manager.swift @@ -43,8 +43,8 @@ package enum Cgroup2Controller: String { // Extremely simple cgroup manager. Our needs are simple for now, and this is // reflected in the type. -package struct Cgroup2Manager: Sendable { - package static let defaultMountPoint = URL(filePath: "/sys/fs/cgroup") +public struct Cgroup2Manager: Sendable { + public static let defaultMountPoint = URL(filePath: "/sys/fs/cgroup") private static let killFile = "cgroup.kill" private static let procsFile = "cgroup.procs" @@ -66,7 +66,7 @@ package struct Cgroup2Manager: Sendable { self.logger = logger } - package static func load( + public static func load( mountPoint: URL = Self.defaultMountPoint, group: URL, logger: Logger? = nil @@ -87,6 +87,26 @@ package struct Cgroup2Manager: Sendable { ) } + package static func loadFromPid(pid: Int32, logger: Logger? = nil) throws -> Cgroup2Manager { + let procCgPath = URL(filePath: "/proc/\(pid)/cgroup") + let fh = try FileHandle(forReadingFrom: procCgPath) + guard let data = try fh.readToEnd() else { + throw Error.errno(errno: errno, message: "failed to read \(procCgPath)") + } + + // If this fails we have bigger problems. + let str = String(data: data, encoding: .utf8)! + let parts = str.split(separator: ":") + if parts[0] != "0" { + throw Error.cgroup1 + } + + // We should really read /proc/pid/mountinfo, but for now just assume + // it's always at /sys/fs/cgroup. + let path = parts[1].trimmingCharacters(in: .whitespacesAndNewlines) + return Cgroup2Manager(group: URL(filePath: String(path)), logger: logger) + } + package func create(perms: Int16 = 0o755) throws { self.logger?.info( "creating cgroup manager", @@ -163,7 +183,7 @@ package struct Cgroup2Manager: Sendable { } } - package func addProcess(pid: Int32) throws { + public func addProcess(pid: Int32) throws { self.logger?.debug( "adding new proc to cgroup", metadata: [ @@ -179,7 +199,7 @@ package struct Cgroup2Manager: Sendable { ) } - package func applyResources(resources: ContainerizationOCI.LinuxResources) throws { + public func applyResources(resources: ContainerizationOCI.LinuxResources) throws { self.logger?.debug( "applying cgroup resources", metadata: [ diff --git a/vminitd/Sources/VminitdCore/ManagedContainer.swift b/vminitd/Sources/VminitdCore/ManagedContainer.swift index 4cb8d82e..9efa5c40 100644 --- a/vminitd/Sources/VminitdCore/ManagedContainer.swift +++ b/vminitd/Sources/VminitdCore/ManagedContainer.swift @@ -91,8 +91,6 @@ public actor ManagedContainer { id: id, stdio: stdio, bundle: bundle, - cgroupManager: cgManager, - resources: spec.linux?.resources, owningPid: nil, log: log ) @@ -178,7 +176,6 @@ extension ManagedContainer { id: id, stdio: stdio, bundle: self.bundle, - cgroupManager: self.cgroupManager, owningPid: self.initProcess.pid, log: self.log ) diff --git a/vminitd/Sources/VminitdCore/ManagedProcess.swift b/vminitd/Sources/VminitdCore/ManagedProcess.swift index 4888e1aa..ba4cd2d1 100644 --- a/vminitd/Sources/VminitdCore/ManagedProcess.swift +++ b/vminitd/Sources/VminitdCore/ManagedProcess.swift @@ -57,8 +57,6 @@ final class ManagedProcess: ContainerProcess, Sendable { private let command: Command private let state: Mutex private let owningPid: Int32? - private let cgroupManager: Cgroup2Manager? - private let resources: ContainerizationOCI.LinuxResources? private let ackPipe: Pipe private let syncPipe: Pipe private let errorPipe: Pipe @@ -75,8 +73,6 @@ final class ManagedProcess: ContainerProcess, Sendable { id: String, stdio: HostStdio, bundle: ContainerizationOCI.Bundle, - cgroupManager: Cgroup2Manager? = nil, - resources: ContainerizationOCI.LinuxResources? = nil, owningPid: Int32? = nil, log: Logger ) throws { @@ -85,8 +81,6 @@ final class ManagedProcess: ContainerProcess, Sendable { log[metadataKey: "id"] = "\(id)" self.log = log self.owningPid = owningPid - self.cgroupManager = cgroupManager - self.resources = resources let syncPipe = Pipe() try syncPipe.setCloexec() @@ -199,11 +193,11 @@ extension ManagedProcess { ]) $0.pid = pid - if let cgroupManager { - if let resources { - try cgroupManager.applyResources(resources: resources) - } - try cgroupManager.addProcess(pid: pid) + // This should probably happen in vmexec, but we don't need to set any cgroup + // toggles so the problem is much simpler to just do it here. + if let owningPid { + let cgManager = try Cgroup2Manager.loadFromPid(pid: owningPid) + try cgManager.addProcess(pid: pid) } log.info( diff --git a/vminitd/Sources/vmexec/RunCommand.swift b/vminitd/Sources/vmexec/RunCommand.swift index 20ffb9a5..4c9607fb 100644 --- a/vminitd/Sources/vmexec/RunCommand.swift +++ b/vminitd/Sources/vmexec/RunCommand.swift @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// import ArgumentParser +import Cgroup import ContainerizationOCI import ContainerizationOS import FoundationEssentials @@ -267,6 +268,20 @@ struct RunCommand: ParsableCommand { if processID == 0 { // child try childSetup(spec: spec, ackPipe: ackPipe, syncPipe: syncPipe) } else { // parent process + // Setup cgroup before child enters cgroup namespace + if let linux = spec.linux { + let cgroupPath = linux.cgroupsPath + if !cgroupPath.isEmpty { + let cgroupManager = try Cgroup2Manager.load(group: URL(filePath: cgroupPath)) + + if let resources = linux.resources { + try cgroupManager.applyResources(resources: resources) + } + + try cgroupManager.addProcess(pid: processID) + } + } + // Send our child's pid before we exit. var childPid = processID try withUnsafeBytes(of: &childPid) { bytes in