Skip to content

Commit d6f4e8d

Browse files
committed
Partial OpenBSD support.
Additional partial platform support. * Create a new CNIOBSD module for OpenBSD, for general cleanliness, and make use of it throughout. Some of the changes are borrowed directly from CNIOLinux; some may technically be unnecessary; I am erring somewhat on expediency to functionality. * Since `malloc_size` is unavailable on OpenBSD, and some of the helpers make use of ManagedBufferPointer which makes use of it, mark some of this as unavailable as well. * Usual pthread optional typing changes, since pthread types are pointers on this platform. * Add conditionals to exclude other functionality not available here, like IP_RECVPKTINFO or IP_PKTINFO. * d_ino is a backwards-compatibility macro valued token for dirent, so instead, just expand with a conditional. * Use kqueue on OpenBSD. This necessitates adding some conditionals for type and feature compatibility. * The vsock API is unavailable on OpenBSD. Tested the NIOTCPEchoClient and NIOTCPEchoServer appears to work and that swift-nio-ssh (hopefully wlog) builds with a local repository with these changes. Because NIOFS/_NIOFileSystem depend on some non-portable components, such as extended attributes, sendfile, non-portable linkat flags, and renameat2, this is only just partial OpenBSD support, which means that `swift build` on the full swift-nio project won't build cleanly, but at least the portable parts can be used to build servers and clients. This commit will mark the linked bug as fixed; I will open a new bug for file system support. Fixes #3383.
1 parent 56724a2 commit d6f4e8d

32 files changed

+412
-20
lines changed

Package.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ let package = Package(
6565
dependencies: [
6666
"NIOConcurrencyHelpers",
6767
"_NIOBase64",
68+
"CNIOBSD",
6869
"CNIODarwin",
6970
"CNIOLinux",
7071
"CNIOWindows",
@@ -97,6 +98,7 @@ let package = Package(
9798
.target(
9899
name: "NIOPosix",
99100
dependencies: [
101+
"CNIOBSD",
100102
"CNIOLinux",
101103
"CNIODarwin",
102104
"CNIOWindows",
@@ -153,6 +155,10 @@ let package = Package(
153155
name: "CNIOSHA1",
154156
dependencies: []
155157
),
158+
.target(
159+
name: "CNIOBSD",
160+
dependencies: [],
161+
),
156162
.target(
157163
name: "CNIOLinux",
158164
dependencies: [],
@@ -488,6 +494,7 @@ let package = Package(
488494
"NIOTestUtils",
489495
"NIOConcurrencyHelpers",
490496
"NIOEmbedded",
497+
"CNIOBSD",
491498
"CNIOLinux",
492499
"CNIODarwin",
493500
"NIOTLS",

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ SwiftNIO aims to support all of the platforms where Swift is supported. Currentl
103103
* Ubuntu 18.04+
104104
* macOS 10.9+, iOS 7+; (macOS 10.14+, iOS 12+, tvOS 12+ or watchOS 6+ with [swift-nio-transport-services][repo-nio-transport-services])
105105

106+
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`.
107+
106108
### Compatibility
107109

108110
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).

Sources/CNIOBSD/include/CNIOBSD.h

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftNIO open source project
4+
//
5+
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
#ifndef C_NIO_BSD_H
15+
#define C_NIO_BSD_H
16+
17+
#include <sys/types.h>
18+
#include <sys/event.h>
19+
#include <sys/socket.h>
20+
#include <sys/stat.h>
21+
#include <sys/time.h>
22+
#include <sys/un.h>
23+
#include <sys/utsname.h>
24+
#include <dirent.h>
25+
#include <fts.h>
26+
#include <ifaddrs.h>
27+
#include <net/if.h>
28+
#include <netinet/in.h>
29+
#include <netinet/ip.h>
30+
#include <pthread.h>
31+
#include <pthread_np.h>
32+
#include <stdbool.h>
33+
34+
35+
// Some explanation is required here.
36+
//
37+
// Due to SR-6772, we cannot get Swift code to directly see any of the mmsg structures or
38+
// functions. However, we *can* get C code built by SwiftPM to see them. For this reason we
39+
// elect to provide a selection of shims to enable Swift code to use recv_mmsg and send_mmsg.
40+
// Mostly this is fine, but to minimise the overhead we want the Swift code to be able to
41+
// create the msgvec directly without requiring further memory fussiness in our C shim.
42+
// That requires us to also construct a C structure that has the same layout as struct mmsghdr.
43+
//
44+
// Conveniently glibc has pretty strict ABI stability rules, and this structure is part of the
45+
// glibc ABI, so we can just reproduce the structure definition here and feel confident that it
46+
// will be sufficient.
47+
//
48+
// If SR-6772 ever gets resolved we can remove this shim.
49+
//
50+
// https://bugs.swift.org/browse/SR-6772
51+
52+
typedef struct {
53+
struct msghdr msg_hdr;
54+
unsigned int msg_len;
55+
} CNIOBSD_mmsghdr;
56+
57+
typedef struct {
58+
struct in6_addr ipi6_addr;
59+
unsigned int ipi6_ifindex;
60+
} CNIOBSD_in6_pktinfo;
61+
62+
int CNIOBSD_sendmmsg(int sockfd, CNIOBSD_mmsghdr *msgvec, unsigned int vlen, int flags);
63+
int CNIOBSD_recvmmsg(int sockfd, CNIOBSD_mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout);
64+
65+
int CNIOBSD_pthread_set_name_np(pthread_t thread, const char *name);
66+
int CNIOBSD_pthread_get_name_np(pthread_t thread, char *name, size_t len);
67+
68+
// Non-standard socket stuff.
69+
int CNIOBSD_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
70+
71+
// cmsghdr handling
72+
struct cmsghdr *CNIOBSD_CMSG_FIRSTHDR(const struct msghdr *);
73+
struct cmsghdr *CNIOBSD_CMSG_NXTHDR(struct msghdr *, struct cmsghdr *);
74+
const void *CNIOBSD_CMSG_DATA(const struct cmsghdr *);
75+
void *CNIOBSD_CMSG_DATA_MUTABLE(struct cmsghdr *);
76+
size_t CNIOBSD_CMSG_LEN(size_t);
77+
size_t CNIOBSD_CMSG_SPACE(size_t);
78+
79+
// awkward time_T pain
80+
extern const int CNIOBSD_SO_TIMESTAMP;
81+
extern const int CNIOBSD_SO_RCVTIMEO;
82+
83+
int CNIOBSD_system_info(struct utsname *uname_data);
84+
85+
extern const unsigned long CNIOBSD_IOCTL_VM_SOCKETS_GET_LOCAL_CID;
86+
87+
const char* CNIOBSD_dirent_dname(struct dirent *ent);
88+
89+
extern const unsigned long CNIOBSD_UTIME_OMIT;
90+
extern const unsigned long CNIOBSD_UTIME_NOW;
91+
92+
extern const long CNIOBSD_UDP_MAX_SEGMENTS;
93+
94+
// A workaround for incorrect nullability annotations in the Android SDK.
95+
// Probably unnecessary on BSD, but copying for consistency for now.
96+
FTS *CNIOBSD_fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **));
97+
98+
#endif

Sources/CNIOBSD/shim.c

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftNIO open source project
4+
//
5+
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
// Xcode's Archive builds with Xcode's Package support struggle with empty .c files
16+
// (https://bugs.swift.org/browse/SR-12939).
17+
void CNIOBSD_i_do_nothing_just_working_around_a_darwin_toolchain_bug(void) {}
18+
19+
#if defined(__OpenBSD__)
20+
21+
#include <CNIOBSD.h>
22+
#include <pthread.h>
23+
#include <assert.h>
24+
#include <unistd.h>
25+
26+
int CNIOBSD_pthread_set_name_np(pthread_t thread, const char *name) {
27+
pthread_set_name_np(thread, name);
28+
return 0;
29+
}
30+
31+
int CNIOBSD_pthread_get_name_np(pthread_t thread, char *name, size_t len) {
32+
pthread_get_name_np(thread, name, len);
33+
return 0;
34+
}
35+
36+
int CNIOBSD_sendmmsg(int sockfd, CNIOBSD_mmsghdr *msgvec, unsigned int vlen, int flags) {
37+
// This is technically undefined behaviour, but it's basically fine because these types are the same size, and we
38+
// don't think the compiler is inclined to blow anything up here.
39+
// This comment is from CNIOLinux, but I haven't reverified this applies for OpenBSD.
40+
return sendmmsg(sockfd, (struct mmsghdr *)msgvec, vlen, flags);
41+
}
42+
43+
int CNIOBSD_recvmmsg(int sockfd, CNIOBSD_mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout) {
44+
// This is technically undefined behaviour, but it's basically fine because these types are the same size, and we
45+
// don't think the compiler is inclined to blow anything up here.
46+
// This comment is from CNIOLinux, but I haven't reverified this applies for OpenBSD.
47+
return recvmmsg(sockfd, (struct mmsghdr *)msgvec, vlen, flags, timeout);
48+
}
49+
50+
int CNIOBSD_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
51+
return accept4(sockfd, addr, addrlen, flags);
52+
}
53+
54+
struct cmsghdr *CNIOBSD_CMSG_FIRSTHDR(const struct msghdr *mhdr) {
55+
assert(mhdr != NULL);
56+
return CMSG_FIRSTHDR(mhdr);
57+
}
58+
59+
struct cmsghdr *CNIOBSD_CMSG_NXTHDR(struct msghdr *mhdr, struct cmsghdr *cmsg) {
60+
assert(mhdr != NULL);
61+
assert(cmsg != NULL);
62+
return CMSG_NXTHDR(mhdr, cmsg);
63+
}
64+
65+
const void *CNIOBSD_CMSG_DATA(const struct cmsghdr *cmsg) {
66+
assert(cmsg != NULL);
67+
return CMSG_DATA(cmsg);
68+
}
69+
70+
void *CNIOBSD_CMSG_DATA_MUTABLE(struct cmsghdr *cmsg) {
71+
assert(cmsg != NULL);
72+
return CMSG_DATA(cmsg);
73+
}
74+
75+
size_t CNIOBSD_CMSG_LEN(size_t payloadSizeBytes) {
76+
return CMSG_LEN(payloadSizeBytes);
77+
}
78+
79+
size_t CNIOBSD_CMSG_SPACE(size_t payloadSizeBytes) {
80+
return CMSG_SPACE(payloadSizeBytes);
81+
}
82+
83+
const int CNIOBSD_SO_TIMESTAMP = SO_TIMESTAMP;
84+
const int CNIOBSD_SO_RCVTIMEO = SO_RCVTIMEO;
85+
86+
bool supports_udp_sockopt(int opt, int value) {
87+
int fd = socket(AF_INET, SOCK_DGRAM, 0);
88+
if (fd == -1) {
89+
return false;
90+
}
91+
int rc = setsockopt(fd, IPPROTO_UDP, opt, &value, sizeof(value));
92+
close(fd);
93+
return rc == 0;
94+
}
95+
96+
bool CNIOBSD_supports_udp_segment() {
97+
#ifndef UDP_SEGMENT
98+
return false;
99+
#else
100+
return supports_udp_sockopt(UDP_SEGMENT, 512);
101+
#endif
102+
}
103+
104+
bool CNIOBSD_supports_udp_gro() {
105+
#ifndef UDP_GRO
106+
return false;
107+
#else
108+
return supports_udp_sockopt(UDP_GRO, 1);
109+
#endif
110+
}
111+
112+
int CNIOBSD_system_info(struct utsname* uname_data) {
113+
return uname(uname_data);
114+
}
115+
116+
const char* CNIOBSD_dirent_dname(struct dirent* ent) {
117+
return ent->d_name;
118+
}
119+
120+
const unsigned long CNIOBSD_UTIME_OMIT = UTIME_OMIT;
121+
const unsigned long CNIOBSD_UTIME_NOW = UTIME_NOW;
122+
123+
#ifdef UDP_MAX_SEGMENTS
124+
const long CNIOBSD_UDP_MAX_SEGMENTS = UDP_MAX_SEGMENTS;
125+
#endif
126+
const long CNIOBSD_UDP_MAX_SEGMENTS = -1;
127+
128+
FTS *CNIOBSD_fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **)) {
129+
return fts_open(path_argv, options, compar);
130+
}
131+
#endif

Sources/CNIOSHA1/c_nio_sha1.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
#endif
5555
#ifdef __ANDROID__
5656
#include <sys/endian.h>
57-
#elif defined(__linux__) || defined(__APPLE__) || defined(__wasm32__)
57+
#elif defined(__linux__) || defined(__APPLE__) || defined(__wasm32__) || defined(__OpenBSD__)
5858
#include <sys/types.h>
5959
#elif defined(_WIN32) || defined(_WIN64)
6060
#ifndef LITTLE_ENDIAN

Sources/NIOConcurrencyHelpers/NIOAtomic.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ public final class NIOAtomic<T: NIOAtomicPrimitive> {
213213

214214
/// Create an atomic object with `value`
215215
@inlinable
216+
@available(OpenBSD, unavailable, message: "malloc_size is unavailable.")
216217
public static func makeAtomic(value: T) -> NIOAtomic {
217218
let manager = Manager(bufferClass: self, minimumCapacity: 1) { _, _ in }
218219
manager.withUnsafeMutablePointerToElements {

Sources/NIOConcurrencyHelpers/NIOLock.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ import wasi_pthread
3535
#if os(Windows)
3636
@usableFromInline
3737
typealias LockPrimitive = SRWLOCK
38+
#elseif os(OpenBSD)
39+
@usableFromInline
40+
typealias LockPrimitive = pthread_mutex_t?
3841
#else
3942
@usableFromInline
4043
typealias LockPrimitive = pthread_mutex_t
@@ -50,6 +53,11 @@ extension LockOperations {
5053

5154
#if os(Windows)
5255
InitializeSRWLock(mutex)
56+
#elseif os(OpenBSD)
57+
var attr = pthread_mutexattr_t(bitPattern: 0)
58+
pthread_mutexattr_init(&attr)
59+
let err = pthread_mutex_init(mutex, &attr)
60+
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
5361
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
5462
var attr = pthread_mutexattr_t()
5563
pthread_mutexattr_init(&attr)

Sources/NIOConcurrencyHelpers/atomics.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ extension UInt: AtomicPrimitive {
463463
deprecated,
464464
message: "AtomicBox is deprecated without replacement because the original implementation doesn't work."
465465
)
466+
@available(OpenBSD, unavailable, message: "malloc_size is unavailable.")
466467
public final class AtomicBox<T: AnyObject> {
467468
private let storage: NIOAtomic<UInt>
468469

@@ -608,4 +609,5 @@ public final class AtomicBox<T: AnyObject> {
608609
}
609610

610611
@available(*, deprecated)
612+
@available(OpenBSD, unavailable, message: "malloc_size is unavailable.")
611613
extension AtomicBox: @unchecked Sendable where T: Sendable {}

Sources/NIOConcurrencyHelpers/lock.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public final class Lock {
4343
#if os(Windows)
4444
fileprivate let mutex: UnsafeMutablePointer<SRWLOCK> =
4545
UnsafeMutablePointer.allocate(capacity: 1)
46+
#elseif os(OpenBSD)
47+
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t?> =
48+
UnsafeMutablePointer.allocate(capacity: 1)
4649
#else
4750
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t> =
4851
UnsafeMutablePointer.allocate(capacity: 1)
@@ -52,6 +55,10 @@ public final class Lock {
5255
public init() {
5356
#if os(Windows)
5457
InitializeSRWLock(self.mutex)
58+
#elseif os(OpenBSD)
59+
var attr = pthread_mutexattr_t(bitPattern: 0)
60+
let err = pthread_mutex_init(self.mutex, &attr)
61+
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
5562
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
5663
var attr = pthread_mutexattr_t()
5764
pthread_mutexattr_init(&attr)
@@ -134,6 +141,9 @@ public final class ConditionLock<T: Equatable> {
134141
#if os(Windows)
135142
private let cond: UnsafeMutablePointer<CONDITION_VARIABLE> =
136143
UnsafeMutablePointer.allocate(capacity: 1)
144+
#elseif os(OpenBSD)
145+
private let cond: UnsafeMutablePointer<pthread_cond_t?> =
146+
UnsafeMutablePointer.allocate(capacity: 1)
137147
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
138148
private let cond: UnsafeMutablePointer<pthread_cond_t> =
139149
UnsafeMutablePointer.allocate(capacity: 1)

Sources/NIOCore/BSDSocketAPI.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ private let sysInet_ntop:
8383
inet_ntop
8484
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> CInt = inet_pton
8585
#endif
86+
#elseif os(OpenBSD)
87+
@preconcurrency import Glibc
88+
import CNIOBSD
89+
90+
private let sysInet_ntop:
91+
@convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer<CChar>?, socklen_t) -> UnsafePointer<CChar>? =
92+
inet_ntop
93+
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> CInt = inet_pton
8694
#elseif canImport(Darwin)
8795
import Darwin
8896

0 commit comments

Comments
 (0)