Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
7 changes: 7 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ let package = Package(
dependencies: [
"NIOConcurrencyHelpers",
"_NIOBase64",
"CNIOBSD",
"CNIODarwin",
"CNIOLinux",
"CNIOWindows",
Expand Down Expand Up @@ -91,6 +92,7 @@ let package = Package(
.target(
name: "NIOPosix",
dependencies: [
"CNIOBSD",
"CNIOLinux",
"CNIODarwin",
"CNIOWindows",
Expand Down Expand Up @@ -147,6 +149,10 @@ let package = Package(
name: "CNIOSHA1",
dependencies: []
),
.target(
name: "CNIOBSD",
dependencies: []
),
.target(
name: "CNIOLinux",
dependencies: [],
Expand Down Expand Up @@ -482,6 +488,7 @@ let package = Package(
"NIOTestUtils",
"NIOConcurrencyHelpers",
"NIOEmbedded",
"CNIOBSD",
"CNIOLinux",
"CNIODarwin",
"NIOTLS",
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ SwiftNIO aims to support all of the platforms where Swift is supported. Currentl
* Ubuntu 18.04+
* macOS 10.9+, iOS 7+; (macOS 10.14+, iOS 12+, tvOS 12+ or watchOS 6+ with [swift-nio-transport-services][repo-nio-transport-services])

SwiftNIO has experimental support on OpenBSD for all SwiftNIO libraries _except_ for NIOFileSystem, which is not yet supported. You can use all other SwiftNIO libraries on OpenBSD by adding them as dependencies in `Package.swift`.

### Compatibility

SwiftNIO follows [SemVer 2.0.0](https://semver.org/#semantic-versioning-200) with a separate document declaring [SwiftNIO's Public API](docs/public-api.md).
Expand Down
98 changes: 98 additions & 0 deletions Sources/CNIOBSD/include/CNIOBSD.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2025 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef C_NIO_BSD_H
#define C_NIO_BSD_H

#include <sys/types.h>
#include <sys/event.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <dirent.h>
#include <fts.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <pthread_np.h>
#include <stdbool.h>


// Some explanation is required here.
//
// Due to SR-6772, we cannot get Swift code to directly see any of the mmsg structures or
// functions. However, we *can* get C code built by SwiftPM to see them. For this reason we
// elect to provide a selection of shims to enable Swift code to use recv_mmsg and send_mmsg.
// Mostly this is fine, but to minimise the overhead we want the Swift code to be able to
// create the msgvec directly without requiring further memory fussiness in our C shim.
// That requires us to also construct a C structure that has the same layout as struct mmsghdr.
//
// Conveniently glibc has pretty strict ABI stability rules, and this structure is part of the
// glibc ABI, so we can just reproduce the structure definition here and feel confident that it
// will be sufficient.
//
// If SR-6772 ever gets resolved we can remove this shim.
//
// https://bugs.swift.org/browse/SR-6772

typedef struct {
struct msghdr msg_hdr;
unsigned int msg_len;
} CNIOBSD_mmsghdr;

typedef struct {
struct in6_addr ipi6_addr;
unsigned int ipi6_ifindex;
} CNIOBSD_in6_pktinfo;

int CNIOBSD_sendmmsg(int sockfd, CNIOBSD_mmsghdr *msgvec, unsigned int vlen, int flags);
int CNIOBSD_recvmmsg(int sockfd, CNIOBSD_mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout);

int CNIOBSD_pthread_set_name_np(pthread_t thread, const char *name);
int CNIOBSD_pthread_get_name_np(pthread_t thread, char *name, size_t len);

// Non-standard socket stuff.
int CNIOBSD_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);

// cmsghdr handling
struct cmsghdr *CNIOBSD_CMSG_FIRSTHDR(const struct msghdr *);
struct cmsghdr *CNIOBSD_CMSG_NXTHDR(struct msghdr *, struct cmsghdr *);
const void *CNIOBSD_CMSG_DATA(const struct cmsghdr *);
void *CNIOBSD_CMSG_DATA_MUTABLE(struct cmsghdr *);
size_t CNIOBSD_CMSG_LEN(size_t);
size_t CNIOBSD_CMSG_SPACE(size_t);

// awkward time_T pain
extern const int CNIOBSD_SO_TIMESTAMP;
extern const int CNIOBSD_SO_RCVTIMEO;

int CNIOBSD_system_info(struct utsname *uname_data);

extern const unsigned long CNIOBSD_IOCTL_VM_SOCKETS_GET_LOCAL_CID;

const char* CNIOBSD_dirent_dname(struct dirent *ent);

extern const unsigned long CNIOBSD_UTIME_OMIT;
extern const unsigned long CNIOBSD_UTIME_NOW;

extern const long CNIOBSD_UDP_MAX_SEGMENTS;

// A workaround for incorrect nullability annotations in the Android SDK.
// Probably unnecessary on BSD, but copying for consistency for now.
FTS *CNIOBSD_fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **));

#endif
131 changes: 131 additions & 0 deletions Sources/CNIOBSD/shim.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2025 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

// Xcode's Archive builds with Xcode's Package support struggle with empty .c files
// (https://bugs.swift.org/browse/SR-12939).
void CNIOBSD_i_do_nothing_just_working_around_a_darwin_toolchain_bug(void) {}

#if defined(__OpenBSD__)

#include <CNIOBSD.h>
#include <pthread.h>
#include <assert.h>
#include <unistd.h>

int CNIOBSD_pthread_set_name_np(pthread_t thread, const char *name) {
pthread_set_name_np(thread, name);
return 0;
}

int CNIOBSD_pthread_get_name_np(pthread_t thread, char *name, size_t len) {
pthread_get_name_np(thread, name, len);
return 0;
}

int CNIOBSD_sendmmsg(int sockfd, CNIOBSD_mmsghdr *msgvec, unsigned int vlen, int flags) {
// This is technically undefined behaviour, but it's basically fine because these types are the same size, and we
// don't think the compiler is inclined to blow anything up here.
// This comment is from CNIOLinux, but I haven't reverified this applies for OpenBSD.
return sendmmsg(sockfd, (struct mmsghdr *)msgvec, vlen, flags);
}

int CNIOBSD_recvmmsg(int sockfd, CNIOBSD_mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout) {
// This is technically undefined behaviour, but it's basically fine because these types are the same size, and we
// don't think the compiler is inclined to blow anything up here.
// This comment is from CNIOLinux, but I haven't reverified this applies for OpenBSD.
return recvmmsg(sockfd, (struct mmsghdr *)msgvec, vlen, flags, timeout);
}

int CNIOBSD_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
return accept4(sockfd, addr, addrlen, flags);
}

struct cmsghdr *CNIOBSD_CMSG_FIRSTHDR(const struct msghdr *mhdr) {
assert(mhdr != NULL);
return CMSG_FIRSTHDR(mhdr);
}

struct cmsghdr *CNIOBSD_CMSG_NXTHDR(struct msghdr *mhdr, struct cmsghdr *cmsg) {
assert(mhdr != NULL);
assert(cmsg != NULL);
return CMSG_NXTHDR(mhdr, cmsg);
}

const void *CNIOBSD_CMSG_DATA(const struct cmsghdr *cmsg) {
assert(cmsg != NULL);
return CMSG_DATA(cmsg);
}

void *CNIOBSD_CMSG_DATA_MUTABLE(struct cmsghdr *cmsg) {
assert(cmsg != NULL);
return CMSG_DATA(cmsg);
}

size_t CNIOBSD_CMSG_LEN(size_t payloadSizeBytes) {
return CMSG_LEN(payloadSizeBytes);
}

size_t CNIOBSD_CMSG_SPACE(size_t payloadSizeBytes) {
return CMSG_SPACE(payloadSizeBytes);
}

const int CNIOBSD_SO_TIMESTAMP = SO_TIMESTAMP;
const int CNIOBSD_SO_RCVTIMEO = SO_RCVTIMEO;

bool supports_udp_sockopt(int opt, int value) {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1) {
return false;
}
int rc = setsockopt(fd, IPPROTO_UDP, opt, &value, sizeof(value));
close(fd);
return rc == 0;
}

bool CNIOBSD_supports_udp_segment() {
#ifndef UDP_SEGMENT
return false;
#else
return supports_udp_sockopt(UDP_SEGMENT, 512);
#endif
}

bool CNIOBSD_supports_udp_gro() {
#ifndef UDP_GRO
return false;
#else
return supports_udp_sockopt(UDP_GRO, 1);
#endif
}

int CNIOBSD_system_info(struct utsname* uname_data) {
return uname(uname_data);
}

const char* CNIOBSD_dirent_dname(struct dirent* ent) {
return ent->d_name;
}

const unsigned long CNIOBSD_UTIME_OMIT = UTIME_OMIT;
const unsigned long CNIOBSD_UTIME_NOW = UTIME_NOW;

#ifdef UDP_MAX_SEGMENTS
const long CNIOBSD_UDP_MAX_SEGMENTS = UDP_MAX_SEGMENTS;
#endif
const long CNIOBSD_UDP_MAX_SEGMENTS = -1;

FTS *CNIOBSD_fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **)) {
return fts_open(path_argv, options, compar);
}
#endif
2 changes: 1 addition & 1 deletion Sources/CNIOSHA1/c_nio_sha1.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
#endif
#ifdef __ANDROID__
#include <sys/endian.h>
#elif defined(__linux__) || defined(__APPLE__) || defined(__wasm32__)
#elif defined(__linux__) || defined(__APPLE__) || defined(__wasm32__) || defined(__OpenBSD__)
#include <sys/types.h>
#elif defined(_WIN32) || defined(_WIN64)
#ifndef LITTLE_ENDIAN
Expand Down
1 change: 1 addition & 0 deletions Sources/NIOConcurrencyHelpers/NIOAtomic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ public final class NIOAtomic<T: NIOAtomicPrimitive> {

/// Create an atomic object with `value`
@inlinable
@available(OpenBSD, unavailable, message: "malloc_size is unavailable.")
public static func makeAtomic(value: T) -> NIOAtomic {
let manager = Manager(bufferClass: self, minimumCapacity: 1) { _, _ in }
manager.withUnsafeMutablePointerToElements {
Expand Down
8 changes: 8 additions & 0 deletions Sources/NIOConcurrencyHelpers/NIOLock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import wasi_pthread
#if os(Windows)
@usableFromInline
typealias LockPrimitive = SRWLOCK
#elseif os(OpenBSD)
@usableFromInline
typealias LockPrimitive = pthread_mutex_t?
#else
@usableFromInline
typealias LockPrimitive = pthread_mutex_t
Expand All @@ -50,6 +53,11 @@ extension LockOperations {

#if os(Windows)
InitializeSRWLock(mutex)
#elseif os(OpenBSD)
var attr = pthread_mutexattr_t(bitPattern: 0)
pthread_mutexattr_init(&attr)
let err = pthread_mutex_init(mutex, &attr)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
var attr = pthread_mutexattr_t()
pthread_mutexattr_init(&attr)
Expand Down
2 changes: 2 additions & 0 deletions Sources/NIOConcurrencyHelpers/atomics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ extension UInt: AtomicPrimitive {
deprecated,
message: "AtomicBox is deprecated without replacement because the original implementation doesn't work."
)
@available(OpenBSD, unavailable, message: "malloc_size is unavailable.")
public final class AtomicBox<T: AnyObject> {
private let storage: NIOAtomic<UInt>

Expand Down Expand Up @@ -608,4 +609,5 @@ public final class AtomicBox<T: AnyObject> {
}

@available(*, deprecated)
@available(OpenBSD, unavailable, message: "malloc_size is unavailable.")
extension AtomicBox: @unchecked Sendable where T: Sendable {}
10 changes: 10 additions & 0 deletions Sources/NIOConcurrencyHelpers/lock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public final class Lock {
#if os(Windows)
fileprivate let mutex: UnsafeMutablePointer<SRWLOCK> =
UnsafeMutablePointer.allocate(capacity: 1)
#elseif os(OpenBSD)
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t?> =
UnsafeMutablePointer.allocate(capacity: 1)
#else
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t> =
UnsafeMutablePointer.allocate(capacity: 1)
Expand All @@ -52,6 +55,10 @@ public final class Lock {
public init() {
#if os(Windows)
InitializeSRWLock(self.mutex)
#elseif os(OpenBSD)
var attr = pthread_mutexattr_t(bitPattern: 0)
let err = pthread_mutex_init(self.mutex, &attr)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
var attr = pthread_mutexattr_t()
pthread_mutexattr_init(&attr)
Expand Down Expand Up @@ -134,6 +141,9 @@ public final class ConditionLock<T: Equatable> {
#if os(Windows)
private let cond: UnsafeMutablePointer<CONDITION_VARIABLE> =
UnsafeMutablePointer.allocate(capacity: 1)
#elseif os(OpenBSD)
private let cond: UnsafeMutablePointer<pthread_cond_t?> =
UnsafeMutablePointer.allocate(capacity: 1)
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
private let cond: UnsafeMutablePointer<pthread_cond_t> =
UnsafeMutablePointer.allocate(capacity: 1)
Expand Down
8 changes: 8 additions & 0 deletions Sources/NIOCore/BSDSocketAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ private let sysInet_ntop:
inet_ntop
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> CInt = inet_pton
#endif
#elseif os(OpenBSD)
@preconcurrency import Glibc
import CNIOBSD

private let sysInet_ntop:
@convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer<CChar>?, socklen_t) -> UnsafePointer<CChar>? =
inet_ntop
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> CInt = inet_pton
#elseif canImport(Darwin)
import Darwin

Expand Down
Loading