Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/linux-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Linux build

permissions:
contents: read

on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches:
- main
- release/*

jobs:
build:
name: Linux compile check
timeout-minutes: 30
runs-on: ubuntu-24.04
container: swift:6.3-noble
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer we install swift in the steps so we can programmatically decide what version to install instead of needing to update this container image manually.


steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
fetch-depth: 0

- name: Install system dependencies
run: apt-get update && apt-get install -y curl make libarchive-dev libbz2-dev liblzma-dev libssl-dev

- name: Build containerization
run: make containerization

- name: Build vminitd (glibc)
run: make -C vminitd SWIFT_CONFIGURATION="--disable-automatic-resolution -Xswiftc -warnings-as-errors"

- name: Install Static Linux SDK
run: make -C vminitd linux-sdk

- name: Build vminitd (musl)
run: make -C vminitd
31 changes: 31 additions & 0 deletions Sources/CShim/include/linux_shim.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright © 2026 Apple Inc. and the Containerization 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.
*/

// The below fall into two main categories:
// 1. Aren't exposed by Swifts glibc modulemap.
// 2. Don't have syscall wrappers/definitions in glibc/musl.

#ifndef __LINUX_SHIM_H
#define __LINUX_SHIM_H

#if defined(__linux__)

#include <sys/epoll.h>
#include <sys/vfs.h>

#endif /* __linux__ */

#endif /* __LINUX_SHIM_H */
37 changes: 28 additions & 9 deletions Sources/ContainerizationOS/Linux/Epoll.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,43 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

#if os(Linux)

#if canImport(Musl)
import Musl
#elseif canImport(Glibc)
import Glibc
#endif

import CShim
import Foundation
import Synchronization

// On glibc, epoll constants are EPOLL_EVENTS enum values. On musl they're
// plain UInt32. These helpers normalize them to UInt32/Int32.
private func epollMask(_ value: UInt32) -> UInt32 { value }
private func epollMask(_ value: Int32) -> UInt32 { UInt32(bitPattern: value) }
#if canImport(Glibc)
private func epollMask(_ value: EPOLL_EVENTS) -> UInt32 { value.rawValue }
private func epollFlag(_ value: EPOLL_EVENTS) -> Int32 { Int32(bitPattern: value.rawValue) }
#endif

/// 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)

public static let maskIn: Int32 = Int32(bitPattern: epollMask(EPOLLIN))
public static let maskOut: Int32 = Int32(bitPattern: epollMask(EPOLLOUT))
public static let defaultMask: Int32 = maskIn | maskOut

private let epollFD: Int32
private let handlers = SafeMap<Int32, Handler>()
private let pipe = Pipe() // to wake up a waiting epoll_wait

public init() throws {
let efd = epoll_create1(EPOLL_CLOEXEC)
let efd = epoll_create1(Int32(EPOLL_CLOEXEC))
guard efd > 0 else {
throw POSIXError.fromErrno()
}
Expand All @@ -41,14 +60,14 @@ public final class Epoll: Sendable {

public func add(
_ fd: Int32,
mask: Int32 = EPOLLIN | EPOLLOUT, // HUP is always added
mask: Int32 = Epoll.defaultMask,
handler: @escaping Handler
) throws {
guard fcntl(fd, F_SETFL, O_NONBLOCK) == 0 else {
throw POSIXError.fromErrno()
}

let events = EPOLLET | UInt32(bitPattern: mask)
let events = epollMask(EPOLLET) | UInt32(bitPattern: mask)

var event = epoll_event()
event.events = events
Expand Down Expand Up @@ -115,7 +134,7 @@ public final class Epoll: Sendable {
public func delete(_ fd: Int32) throws {
var event = epoll_event()
let result = withUnsafeMutablePointer(to: &event) { ptr in
epoll_ctl(self.epollFD, EPOLL_CTL_DEL, fd, ptr)
epoll_ctl(self.epollFD, EPOLL_CTL_DEL, fd, ptr) as Int32
}
if result != 0 {
if !acceptableDeletionErrno() {
Expand Down Expand Up @@ -162,20 +181,20 @@ public final class Epoll: Sendable {

extension Epoll.Mask {
public var isHangup: Bool {
(self & (EPOLLHUP | EPOLLERR)) != 0
(self & Int32(bitPattern: epollMask(EPOLLHUP) | epollMask(EPOLLERR))) != 0
}

public var isRhangup: Bool {
(self & EPOLLRDHUP) != 0
(self & Int32(bitPattern: epollMask(EPOLLRDHUP))) != 0
}

public var readyToRead: Bool {
(self & EPOLLIN) != 0
(self & Int32(bitPattern: epollMask(EPOLLIN))) != 0
}

public var readyToWrite: Bool {
(self & EPOLLOUT) != 0
(self & Int32(bitPattern: epollMask(EPOLLOUT))) != 0
}
}

#endif // canImport(Musl)
#endif // os(Linux)
7 changes: 4 additions & 3 deletions vminitd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ SWIFT_CONFIGURATION := --swift-sdk $(MUSL_ARCH)-swift-linux-musl $(SWIFT_WARNING

SWIFT_VERSION := 6.3
SWIFT_SDK_URL := https://download.swift.org/swift-6.3-release/static-sdk/swift-6.3-RELEASE/swift-6.3-RELEASE_static-linux-0.1.0.artifactbundle.tar.gz
SWIFT_SDK_CHECKSUM := d2078b69bdeb5c31202c10e9d8a11d6f66f82938b51a4b75f032ccb35c4c286c
SWIFT_SDK_PATH := /tmp/$(notdir $(SWIFT_SDK_URL))

SYSTEM_TYPE := $(shell uname -s)
Expand Down Expand Up @@ -64,7 +65,7 @@ all:
@install "$(BUILD_BIN_DIR)/vmexec" ./bin/

.PHONY: cross-prep
cross-prep: linux-sdk
cross-prep: swift linux-sdk

.PHONY: swiftly
swiftly:
Expand All @@ -84,10 +85,10 @@ swift: swiftly
@${SWIFTLY_BIN_DIR}/swiftly install $(SWIFT_VERSION)

.PHONY: linux-sdk
linux-sdk: swift
linux-sdk:
@echo Installing Static Linux SDK...
@curl -L -o $(SWIFT_SDK_PATH) $(SWIFT_SDK_URL)
-@$(SWIFT) sdk install $(SWIFT_SDK_PATH)
-@$(SWIFT) sdk install $(SWIFT_SDK_PATH) --checksum $(SWIFT_SDK_CHECKSUM)
@rm $(SWIFT_SDK_PATH)

.PHONY: clean
Expand Down
1 change: 1 addition & 0 deletions vminitd/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ let package = Package(
.product(name: "ContainerizationOCI", package: "containerization"),
.product(name: "ContainerizationOS", package: "containerization"),
.product(name: "SystemPackage", package: "swift-system"),
"LCShim",
]
),
.executableTarget(
Expand Down
1 change: 1 addition & 0 deletions vminitd/Sources/Cgroup/Cgroup2Manager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Musl
import Glibc
#endif

import LCShim
import ContainerizationOCI
import ContainerizationOS
import Foundation
Expand Down
68 changes: 65 additions & 3 deletions vminitd/Sources/LCShim/include/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,87 @@
* limitations under the License.
*/

// The below fall into two main categories:
// 1. Aren't exposed by Swifts glibc modulemap.
// 2. Don't have syscall wrappers/definitions in glibc/musl.

#ifndef __SYSCALL_H
#define __SYSCALL_H

#include <sys/types.h>
#include <sys/vfs.h>

int CZ_pivot_root(const char *new_root, const char *put_old);
// CLONE_* flags
#ifndef CLONE_NEWNS
#define CLONE_NEWNS 0x00020000
#endif
#ifndef CLONE_NEWCGROUP
#define CLONE_NEWCGROUP 0x02000000
#endif
#ifndef CLONE_NEWUTS
#define CLONE_NEWUTS 0x04000000
#endif
#ifndef CLONE_NEWIPC
#define CLONE_NEWIPC 0x08000000
#endif
#ifndef CLONE_NEWUSER
#define CLONE_NEWUSER 0x10000000
#endif
#ifndef CLONE_NEWPID
#define CLONE_NEWPID 0x20000000
#endif

extern int setns(int fd, int nstype);
extern int unshare(int flags);
extern int dup3(int oldfd, int newfd, int flags);
extern int execvpe(const char *file, char *const argv[], char *const envp[]);
extern int unlockpt(int fd);
extern char *ptsname(int fd);

// splice(2) and flags.
extern ssize_t splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out,
size_t len, unsigned int flags);
#ifndef SPLICE_F_MOVE
#define SPLICE_F_MOVE 1
#endif
#ifndef SPLICE_F_NONBLOCK
#define SPLICE_F_NONBLOCK 2
#endif

// RLIMIT constants as plain integers. On glibc these are __rlimit_resource
// enum values which can't be used as Int32 in Swift.
#define CZ_RLIMIT_CPU 0
#define CZ_RLIMIT_FSIZE 1
#define CZ_RLIMIT_DATA 2
#define CZ_RLIMIT_STACK 3
#define CZ_RLIMIT_CORE 4
#define CZ_RLIMIT_RSS 5
#define CZ_RLIMIT_NPROC 6
#define CZ_RLIMIT_NOFILE 7
#define CZ_RLIMIT_MEMLOCK 8
#define CZ_RLIMIT_AS 9
#define CZ_RLIMIT_LOCKS 10
#define CZ_RLIMIT_SIGPENDING 11
#define CZ_RLIMIT_MSGQUEUE 12
#define CZ_RLIMIT_NICE 13
#define CZ_RLIMIT_RTPRIO 14
#define CZ_RLIMIT_RTTIME 15

// setrlimit wrapper that accepts plain int for the resource parameter,
// avoiding glibc's __rlimit_resource enum type mismatch in Swift.
int CZ_setrlimit(int resource, unsigned long long soft, unsigned long long hard);

int CZ_pivot_root(const char *new_root, const char *put_old);
int CZ_set_sub_reaper();

#ifndef SYS_pidfd_open
#define SYS_pidfd_open 434
#endif

int CZ_pidfd_open(pid_t pid, unsigned int flags);

#ifndef SYS_pidfd_getfd
#define SYS_pidfd_getfd 438
#endif

int CZ_pidfd_getfd(int pidfd, int targetfd, unsigned int flags);

int CZ_prctl_set_no_new_privs();
Expand Down
9 changes: 9 additions & 0 deletions vminitd/Sources/LCShim/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <unistd.h>

Expand All @@ -39,3 +40,11 @@ int CZ_pidfd_getfd(int pidfd, int targetfd, unsigned int flags) {
int CZ_prctl_set_no_new_privs() {
return prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
}

int CZ_setrlimit(int resource, unsigned long long soft,
unsigned long long hard) {
struct rlimit limit;
limit.rlim_cur = (rlim_t)soft;
limit.rlim_max = (rlim_t)hard;
return setrlimit(resource, &limit);
}
1 change: 1 addition & 0 deletions vminitd/Sources/vmexec/Console.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//

import FoundationEssentials
import LCShim

#if canImport(Musl)
import Musl
Expand Down
2 changes: 1 addition & 1 deletion vminitd/Sources/vmexec/RunCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ struct RunCommand: ParsableCommand {
guard statfs("/", &s) == 0 else {
throw App.Errno(stage: "statfs(/)")
}
flags |= s.f_flags
flags |= UInt(s.f_flags)

guard mount("", "/", "", flags, "") == 0 else {
throw App.Errno(stage: "mount rootfs ro")
Expand Down
Loading
Loading