/src/swift-nio/Sources/NIOPosix/System.swift
Line | Count | Source |
1 | | //===----------------------------------------------------------------------===// |
2 | | // |
3 | | // This source file is part of the SwiftNIO open source project |
4 | | // |
5 | | // Copyright (c) 2017-2024 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 | | // This file contains code that ensures errno is captured correctly when doing syscalls and no ARC traffic can happen inbetween that *could* change the errno |
15 | | // value before we were able to read it. |
16 | | // It's important that all static methods are declared with `@inline(never)` so it's not possible any ARC traffic happens while we need to read errno. |
17 | | |
18 | | #if !os(WASI) |
19 | | |
20 | | import NIOCore |
21 | | |
22 | | #if canImport(Darwin) |
23 | | @_exported import Darwin.C |
24 | | import CNIODarwin |
25 | | internal typealias MMsgHdr = CNIODarwin_mmsghdr |
26 | | #elseif os(Linux) || os(FreeBSD) || os(Android) |
27 | | #if canImport(Glibc) |
28 | | @_exported @preconcurrency import Glibc |
29 | | #elseif canImport(Musl) |
30 | | @_exported @preconcurrency import Musl |
31 | | #elseif canImport(Android) |
32 | | @_exported @preconcurrency import Android |
33 | | #endif |
34 | | import CNIOLinux |
35 | | internal typealias MMsgHdr = CNIOLinux_mmsghdr |
36 | | internal typealias in6_pktinfo = CNIOLinux_in6_pktinfo |
37 | | #elseif os(OpenBSD) |
38 | | @_exported @preconcurrency import Glibc |
39 | | import CNIOOpenBSD |
40 | | internal typealias MMsgHdr = CNIOOpenBSD_mmsghdr |
41 | | let INADDR_ANY = UInt32(0) |
42 | | #elseif os(Windows) |
43 | | @_exported import ucrt |
44 | | |
45 | | import CNIOWindows |
46 | | |
47 | | internal typealias MMsgHdr = CNIOWindows_mmsghdr |
48 | | #else |
49 | | #error("The POSIX system module was unable to identify your C library.") |
50 | | #endif |
51 | | |
52 | | #if os(Android) |
53 | | let INADDR_ANY = UInt32(0) // #define INADDR_ANY ((unsigned long int) 0x00000000) |
54 | | let IFF_BROADCAST: CUnsignedInt = numericCast(Android.IFF_BROADCAST.rawValue) |
55 | | let IFF_POINTOPOINT: CUnsignedInt = numericCast(Android.IFF_POINTOPOINT.rawValue) |
56 | | let IFF_MULTICAST: CUnsignedInt = numericCast(Android.IFF_MULTICAST.rawValue) |
57 | | internal typealias in_port_t = UInt16 |
58 | | extension ipv6_mreq { // http://lkml.iu.edu/hypermail/linux/kernel/0106.1/0080.html |
59 | | init(ipv6mr_multiaddr: in6_addr, ipv6mr_interface: UInt32) { |
60 | | self.init( |
61 | | ipv6mr_multiaddr: ipv6mr_multiaddr, |
62 | | ipv6mr_ifindex: Int32(bitPattern: ipv6mr_interface) |
63 | | ) |
64 | | } |
65 | | } |
66 | | #if arch(arm) |
67 | | let S_IFSOCK = UInt32(Android.S_IFSOCK) |
68 | | let S_IFMT = UInt32(Android.S_IFMT) |
69 | | let S_IFREG = UInt32(Android.S_IFREG) |
70 | | let S_IFDIR = UInt32(Android.S_IFDIR) |
71 | | let S_IFLNK = UInt32(Android.S_IFLNK) |
72 | | let S_IFBLK = UInt32(Android.S_IFBLK) |
73 | | #endif |
74 | | #endif |
75 | | |
76 | | // Declare aliases to share more code and not need to repeat #if #else blocks |
77 | | #if !os(Windows) |
78 | | private let sysClose = close |
79 | | private let sysShutdown = shutdown |
80 | | private let sysBind = bind |
81 | 0 | private let sysFcntl: @Sendable @convention(c) (CInt, CInt, CInt) -> CInt = { fcntl($0, $1, $2) } |
82 | | private let sysSocket = socket |
83 | | private let sysSetsockopt = setsockopt |
84 | | private let sysGetsockopt = getsockopt |
85 | | private let sysListen = listen |
86 | | private let sysAccept = accept |
87 | | private let sysConnect = connect |
88 | 0 | private let sysOpen: @Sendable @convention(c) (UnsafePointer<CChar>, CInt) -> CInt = { open($0, $1) } |
89 | 0 | private let sysOpenWithMode: @Sendable @convention(c) (UnsafePointer<CChar>, CInt, mode_t) -> CInt = { |
90 | 0 | open($0, $1, $2) |
91 | 0 | } |
92 | | private let sysFtruncate = ftruncate |
93 | | private let sysWrite = write |
94 | | private let sysPwrite = pwrite |
95 | | private let sysRead = read |
96 | | private let sysPread = pread |
97 | | private let sysLseek = lseek |
98 | | private let sysPoll = poll |
99 | | #else |
100 | | private let sysWrite = _write |
101 | | private let sysRead = _read |
102 | | private let sysLseek = _lseek |
103 | | private let sysFtruncate = _chsize_s |
104 | | #endif |
105 | | |
106 | | #if os(Android) |
107 | | func sysRecvFrom_wrapper( |
108 | | sockfd: CInt, |
109 | | buf: UnsafeMutableRawPointer, |
110 | | len: CLong, |
111 | | flags: CInt, |
112 | | src_addr: UnsafeMutablePointer<sockaddr>, |
113 | | addrlen: UnsafeMutablePointer<socklen_t> |
114 | | ) -> CLong { |
115 | | // src_addr is 'UnsafeMutablePointer', but it need to be 'UnsafePointer' |
116 | | recvfrom(sockfd, buf, len, flags, src_addr, addrlen) |
117 | | // src_addr is 'UnsafeMutablePointer', but it need to be 'UnsafePointer' |
118 | | } |
119 | | func sysWritev_wrapper(fd: CInt, iov: UnsafePointer<iovec>?, iovcnt: CInt) -> CLong { |
120 | | CLong(writev(fd, iov!, iovcnt)) // cast 'Int32' to 'CLong'// cast 'Int32' to 'CLong' |
121 | | } |
122 | | private let sysWritev = sysWritev_wrapper |
123 | | #elseif !os(Windows) |
124 | | private let sysWritev: @convention(c) (Int32, UnsafePointer<iovec>?, CInt) -> CLong = writev |
125 | | #endif |
126 | | #if canImport(Android) |
127 | | private let sysRecvMsg: @convention(c) (CInt, UnsafeMutablePointer<msghdr>, CInt) -> ssize_t = recvmsg |
128 | | private let sysSendMsg: @convention(c) (CInt, UnsafePointer<msghdr>, CInt) -> ssize_t = sendmsg |
129 | | #elseif !os(Windows) |
130 | | private let sysRecvMsg: @convention(c) (CInt, UnsafeMutablePointer<msghdr>?, CInt) -> ssize_t = recvmsg |
131 | | private let sysSendMsg: @convention(c) (CInt, UnsafePointer<msghdr>?, CInt) -> ssize_t = sendmsg |
132 | | #endif |
133 | | #if os(Windows) |
134 | | private let sysDup: @convention(c) (CInt) -> CInt = _dup |
135 | | #else |
136 | | private let sysDup: @convention(c) (CInt) -> CInt = dup |
137 | | #endif |
138 | | #if canImport(Android) |
139 | | private let sysGetpeername: |
140 | | @convention(c) (CInt, UnsafeMutablePointer<sockaddr>, UnsafeMutablePointer<socklen_t>) -> CInt = getpeername |
141 | | private let sysGetsockname: |
142 | | @convention(c) (CInt, UnsafeMutablePointer<sockaddr>, UnsafeMutablePointer<socklen_t>) -> CInt = getsockname |
143 | | #elseif !os(Windows) |
144 | | private let sysGetpeername: |
145 | | @convention(c) (CInt, UnsafeMutablePointer<sockaddr>?, UnsafeMutablePointer<socklen_t>?) -> CInt = getpeername |
146 | | private let sysGetsockname: |
147 | | @convention(c) (CInt, UnsafeMutablePointer<sockaddr>?, UnsafeMutablePointer<socklen_t>?) -> CInt = getsockname |
148 | | #endif |
149 | | |
150 | | #if os(Android) |
151 | | private let sysIfNameToIndex: @convention(c) (UnsafePointer<CChar>) -> CUnsignedInt = if_nametoindex |
152 | | #else |
153 | | private let sysIfNameToIndex: @convention(c) (UnsafePointer<CChar>?) -> CUnsignedInt = if_nametoindex |
154 | | #endif |
155 | | #if canImport(Android) |
156 | | private let sysSocketpair: @convention(c) (CInt, CInt, CInt, UnsafeMutablePointer<CInt>) -> CInt = socketpair |
157 | | #elseif !os(Windows) |
158 | | private let sysSocketpair: @convention(c) (CInt, CInt, CInt, UnsafeMutablePointer<CInt>?) -> CInt = socketpair |
159 | | #endif |
160 | | |
161 | | #if os(Linux) || os(Android) || canImport(Darwin) || os(OpenBSD) |
162 | | private let sysFstat = fstat |
163 | | private let sysStat = stat |
164 | | private let sysLstat = lstat |
165 | | private let sysSymlink = symlink |
166 | | private let sysReadlink = readlink |
167 | | private let sysUnlink = unlink |
168 | | private let sysMkdir = mkdir |
169 | | private let sysOpendir = opendir |
170 | | private let sysReaddir = readdir |
171 | | private let sysClosedir = closedir |
172 | | private let sysRename = rename |
173 | | private let sysRemove = remove |
174 | | #endif |
175 | | #if os(Linux) || os(Android) |
176 | | private let sysSendMmsg = CNIOLinux_sendmmsg |
177 | | private let sysRecvMmsg = CNIOLinux_recvmmsg |
178 | | #elseif os(OpenBSD) |
179 | | private let sysKevent = kevent |
180 | | private let sysSendMmsg = CNIOOpenBSD_sendmmsg |
181 | | private let sysRecvMmsg = CNIOOpenBSD_recvmmsg |
182 | | #elseif canImport(Darwin) |
183 | | private let sysKevent = kevent |
184 | | private let sysMkpath = mkpath_np |
185 | | private let sysSendMmsg = CNIODarwin_sendmmsg |
186 | | private let sysRecvMmsg = CNIODarwin_recvmmsg |
187 | | #endif |
188 | | #if !os(Windows) |
189 | | private let sysIoctl: @convention(c) (CInt, CUnsignedLong, UnsafeMutableRawPointer) -> CInt = ioctl |
190 | | #endif // !os(Windows) |
191 | | |
192 | | @inlinable |
193 | 0 | func isUnacceptableErrno(_ code: CInt) -> Bool { |
194 | 0 | // On iOS, EBADF is a possible result when a file descriptor has been reaped in the background. |
195 | 0 | // In particular, it's possible to get EBADF from accept(), where the underlying accept() FD |
196 | 0 | // is valid but the accepted one is not. The right solution here is to perform a check for |
197 | 0 | // SO_ISDEFUNCT when we see this happen, but we haven't yet invested the time to do that. |
198 | 0 | // In the meantime, we just tolerate EBADF on iOS. |
199 | | #if canImport(Darwin) && !os(macOS) |
200 | | switch code { |
201 | | case EFAULT: |
202 | | return true |
203 | | default: |
204 | | return false |
205 | | } |
206 | | #else |
207 | 0 | switch code { |
208 | 0 | case EFAULT, EBADF: |
209 | 0 | return true |
210 | 0 | default: |
211 | 0 | return false |
212 | 0 | } |
213 | | #endif |
214 | 0 | } |
215 | | |
216 | | @inlinable |
217 | 0 | public func isUnacceptableErrnoOnClose(_ code: CInt) -> Bool { |
218 | 0 | // We treat close() differently to all other FDs: we still want to catch EBADF here. |
219 | 0 | switch code { |
220 | 0 | case EFAULT, EBADF: |
221 | 0 | return true |
222 | 0 | default: |
223 | 0 | return false |
224 | 0 | } |
225 | 0 | } |
226 | | |
227 | | @inlinable |
228 | 0 | internal func isUnacceptableErrnoForbiddingEINVAL(_ code: CInt) -> Bool { |
229 | 0 | // We treat read() and pread() differently since we also want to catch EINVAL. |
230 | | #if canImport(Darwin) && !os(macOS) |
231 | | switch code { |
232 | | case EFAULT, EINVAL: |
233 | | return true |
234 | | default: |
235 | | return false |
236 | | } |
237 | | #else |
238 | 0 | switch code { |
239 | 0 | case EFAULT, EBADF, EINVAL: |
240 | 0 | return true |
241 | 0 | default: |
242 | 0 | return false |
243 | 0 | } |
244 | | #endif |
245 | 0 | } |
246 | | |
247 | | #if os(Windows) |
248 | | @inlinable |
249 | | internal func strerror(_ errno: CInt) -> String { |
250 | | withUnsafeTemporaryAllocation(of: CChar.self, capacity: 95) { |
251 | | let result = strerror_s($0.baseAddress, $0.count, errno) |
252 | | guard result == 0 else { return "Unknown error: \(errno)" } |
253 | | return String(cString: $0.baseAddress!) |
254 | | } |
255 | | } |
256 | | #endif |
257 | | |
258 | | @inlinable |
259 | 0 | internal func preconditionIsNotUnacceptableErrno(err: CInt, where function: String) { |
260 | 0 | // strerror is documented to return "Unknown error: ..." for illegal value so it won't ever fail |
261 | | #if os(Windows) |
262 | | precondition(!isUnacceptableErrno(err), "unacceptable errno \(err) \(strerror(err)) in \(function))") |
263 | | #else |
264 | 0 | precondition( |
265 | 0 | !isUnacceptableErrno(err), |
266 | 0 | "unacceptable errno \(err) \(String(cString: strerror(err)!)) in \(function))" |
267 | 0 | ) |
268 | | #endif |
269 | 0 | } |
270 | | |
271 | | @inlinable |
272 | 0 | internal func preconditionIsNotUnacceptableErrnoOnClose(err: CInt, where function: String) { |
273 | 0 | // strerror is documented to return "Unknown error: ..." for illegal value so it won't ever fail |
274 | | #if os(Windows) |
275 | | precondition(!isUnacceptableErrnoOnClose(err), "unacceptable errno \(err) \(strerror(err)) in \(function))") |
276 | | #else |
277 | 0 | precondition( |
278 | 0 | !isUnacceptableErrnoOnClose(err), |
279 | 0 | "unacceptable errno \(err) \(String(cString: strerror(err)!)) in \(function))" |
280 | 0 | ) |
281 | | #endif |
282 | 0 | } |
283 | | |
284 | | @inlinable |
285 | 0 | internal func preconditionIsNotUnacceptableErrnoForbiddingEINVAL(err: CInt, where function: String) { |
286 | 0 | // strerror is documented to return "Unknown error: ..." for illegal value so it won't ever fail |
287 | | #if os(Windows) |
288 | | precondition( |
289 | | !isUnacceptableErrnoForbiddingEINVAL(err), |
290 | | "unacceptable errno \(err) \(strerror(err)) in \(function))" |
291 | | ) |
292 | | #else |
293 | 0 | precondition( |
294 | 0 | !isUnacceptableErrnoForbiddingEINVAL(err), |
295 | 0 | "unacceptable errno \(err) \(String(cString: strerror(err)!)) in \(function))" |
296 | 0 | ) |
297 | | #endif |
298 | 0 | } |
299 | | |
300 | | // Sorry, we really try hard to not use underscored attributes. In this case |
301 | | // however we seem to break the inlining threshold which makes a system call |
302 | | // take twice the time, ie. we need this exception. |
303 | | @inline(__always) |
304 | | @discardableResult |
305 | | @inlinable |
306 | | internal func syscall<T: FixedWidthInteger>( |
307 | | blocking: Bool, |
308 | | where function: String = #function, |
309 | | _ body: () throws -> T |
310 | | ) |
311 | | throws -> IOResult<T> |
312 | 0 | { |
313 | 0 | while true { |
314 | 0 | let res = try body() |
315 | 0 | if res == -1 { |
316 | | #if os(Windows) |
317 | | var err: CInt = 0 |
318 | | _get_errno(&err) |
319 | | #else |
320 | 0 | let err = errno |
321 | | #endif |
322 | 0 | switch (err, blocking) { |
323 | 0 | case (EINTR, _): |
324 | 0 | continue |
325 | 0 | case (EWOULDBLOCK, true): |
326 | 0 | return .wouldBlock(0) |
327 | 0 | default: |
328 | 0 | preconditionIsNotUnacceptableErrno(err: err, where: function) |
329 | 0 | throw IOError(errnoCode: err, reason: function) |
330 | 0 | } |
331 | 0 | } |
332 | 0 | return .processed(res) |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | | #if canImport(Darwin) |
337 | | @inline(__always) |
338 | | @inlinable |
339 | | @discardableResult |
340 | | internal func syscall<T>( |
341 | | where function: String = #function, |
342 | | _ body: () throws -> UnsafeMutablePointer<T>? |
343 | | ) |
344 | | throws -> UnsafeMutablePointer<T> |
345 | | { |
346 | | while true { |
347 | | if let res = try body() { |
348 | | return res |
349 | | } else { |
350 | | let err = errno |
351 | | switch err { |
352 | | case EINTR: |
353 | | continue |
354 | | default: |
355 | | preconditionIsNotUnacceptableErrno(err: err, where: function) |
356 | | throw IOError(errnoCode: err, reason: function) |
357 | | } |
358 | | } |
359 | | } |
360 | | } |
361 | | #elseif os(Linux) || os(Android) || os(OpenBSD) |
362 | | @inline(__always) |
363 | | @inlinable |
364 | | @discardableResult |
365 | | internal func syscall( |
366 | | where function: String = #function, |
367 | | _ body: () throws -> OpaquePointer? |
368 | | ) |
369 | | throws -> OpaquePointer |
370 | 0 | { |
371 | 0 | while true { |
372 | 0 | if let res = try body() { |
373 | 0 | return res |
374 | 0 | } else { |
375 | 0 | let err = errno |
376 | 0 | switch err { |
377 | 0 | case EINTR: |
378 | 0 | continue |
379 | 0 | default: |
380 | 0 | preconditionIsNotUnacceptableErrno(err: err, where: function) |
381 | 0 | throw IOError(errnoCode: err, reason: function) |
382 | 0 | } |
383 | 0 | } |
384 | 0 | } |
385 | 0 | } |
386 | | #endif |
387 | | |
388 | | #if !os(Windows) |
389 | | @inline(__always) |
390 | | @inlinable |
391 | | @discardableResult |
392 | | internal func syscallOptional<T>( |
393 | | where function: String = #function, |
394 | | _ body: () throws -> UnsafeMutablePointer<T>? |
395 | | ) |
396 | | throws -> UnsafeMutablePointer<T>? |
397 | 0 | { |
398 | 0 | while true { |
399 | 0 | errno = 0 |
400 | 0 | if let res = try body() { |
401 | 0 | return res |
402 | 0 | } else { |
403 | 0 | let err = errno |
404 | 0 | switch err { |
405 | 0 | case 0: |
406 | 0 | return nil |
407 | 0 | case EINTR: |
408 | 0 | continue |
409 | 0 | default: |
410 | 0 | preconditionIsNotUnacceptableErrno(err: err, where: function) |
411 | 0 | throw IOError(errnoCode: err, reason: function) |
412 | 0 | } |
413 | 0 | } |
414 | 0 | } |
415 | 0 | } |
416 | | #endif |
417 | | |
418 | | // Sorry, we really try hard to not use underscored attributes. In this case |
419 | | // however we seem to break the inlining threshold which makes a system call |
420 | | // take twice the time, ie. we need this exception. |
421 | | @inline(__always) |
422 | | @inlinable |
423 | | @discardableResult |
424 | | internal func syscallForbiddingEINVAL<T: FixedWidthInteger>( |
425 | | where function: String = #function, |
426 | | _ body: () throws -> T |
427 | | ) |
428 | | throws -> IOResult<T> |
429 | 0 | { |
430 | 0 | while true { |
431 | 0 | let res = try body() |
432 | 0 | if res == -1 { |
433 | | #if os(Windows) |
434 | | var err: CInt = 0 |
435 | | _get_errno(&err) |
436 | | #else |
437 | 0 | let err = errno |
438 | | #endif |
439 | 0 | switch err { |
440 | 0 | case EINTR: |
441 | 0 | continue |
442 | 0 | case EWOULDBLOCK: |
443 | 0 | return .wouldBlock(0) |
444 | 0 | default: |
445 | 0 | preconditionIsNotUnacceptableErrnoForbiddingEINVAL(err: err, where: function) |
446 | 0 | throw IOError(errnoCode: err, reason: function) |
447 | 0 | } |
448 | 0 | } |
449 | 0 | return .processed(res) |
450 | 0 | } |
451 | 0 | } |
452 | | |
453 | | @usableFromInline |
454 | | internal enum Posix: Sendable { |
455 | | #if canImport(Darwin) |
456 | | @usableFromInline |
457 | | static let UIO_MAXIOV: Int = 1024 |
458 | | @usableFromInline |
459 | | static let SHUT_RD: CInt = CInt(Darwin.SHUT_RD) |
460 | | @usableFromInline |
461 | | static let SHUT_WR: CInt = CInt(Darwin.SHUT_WR) |
462 | | @usableFromInline |
463 | | static let SHUT_RDWR: CInt = CInt(Darwin.SHUT_RDWR) |
464 | | #elseif os(Linux) || os(FreeBSD) || os(Android) || os(OpenBSD) |
465 | | #if canImport(Glibc) |
466 | | @usableFromInline |
467 | | static let UIO_MAXIOV: Int = Int(Glibc.UIO_MAXIOV) |
468 | | @usableFromInline |
469 | | static let SHUT_RD: CInt = CInt(Glibc.SHUT_RD) |
470 | | @usableFromInline |
471 | | static let SHUT_WR: CInt = CInt(Glibc.SHUT_WR) |
472 | | @usableFromInline |
473 | | static let SHUT_RDWR: CInt = CInt(Glibc.SHUT_RDWR) |
474 | | #elseif canImport(Musl) |
475 | | @usableFromInline |
476 | | static let UIO_MAXIOV: Int = Int(Musl.UIO_MAXIOV) |
477 | | @usableFromInline |
478 | | static let SHUT_RD: CInt = CInt(Musl.SHUT_RD) |
479 | | @usableFromInline |
480 | | static let SHUT_WR: CInt = CInt(Musl.SHUT_WR) |
481 | | @usableFromInline |
482 | | static let SHUT_RDWR: CInt = CInt(Musl.SHUT_RDWR) |
483 | | #elseif canImport(Android) |
484 | | @usableFromInline |
485 | | static let UIO_MAXIOV: Int = Int(Android.UIO_MAXIOV) |
486 | | @usableFromInline |
487 | | static let SHUT_RD: CInt = CInt(Android.SHUT_RD) |
488 | | @usableFromInline |
489 | | static let SHUT_WR: CInt = CInt(Android.SHUT_WR) |
490 | | @usableFromInline |
491 | | static let SHUT_RDWR: CInt = CInt(Android.SHUT_RDWR) |
492 | | #endif |
493 | | #else |
494 | | @usableFromInline |
495 | | static var UIO_MAXIOV: Int { |
496 | | fatalError("unsupported OS") |
497 | | } |
498 | | @usableFromInline |
499 | | static var SHUT_RD: Int { |
500 | | fatalError("unsupported OS") |
501 | | } |
502 | | @usableFromInline |
503 | | static var SHUT_WR: Int { |
504 | | fatalError("unsupported OS") |
505 | | } |
506 | | @usableFromInline |
507 | | static var SHUT_RDWR: Int { |
508 | | fatalError("unsupported OS") |
509 | | } |
510 | | #endif |
511 | | |
512 | | #if canImport(Darwin) |
513 | | static let IPTOS_ECN_NOTECT: CInt = CNIODarwin_IPTOS_ECN_NOTECT |
514 | | static let IPTOS_ECN_MASK: CInt = CNIODarwin_IPTOS_ECN_MASK |
515 | | static let IPTOS_ECN_ECT0: CInt = CNIODarwin_IPTOS_ECN_ECT0 |
516 | | static let IPTOS_ECN_ECT1: CInt = CNIODarwin_IPTOS_ECN_ECT1 |
517 | | static let IPTOS_ECN_CE: CInt = CNIODarwin_IPTOS_ECN_CE |
518 | | #elseif os(Linux) || os(FreeBSD) || os(Android) |
519 | | #if os(Android) |
520 | | static let IPTOS_ECN_NOTECT: CInt = CInt(CNIOLinux.IPTOS_ECN_NOTECT) |
521 | | #else |
522 | | static let IPTOS_ECN_NOTECT: CInt = CInt(CNIOLinux.IPTOS_ECN_NOT_ECT) |
523 | | #endif |
524 | | static let IPTOS_ECN_MASK: CInt = CInt(CNIOLinux.IPTOS_ECN_MASK) |
525 | | static let IPTOS_ECN_ECT0: CInt = CInt(CNIOLinux.IPTOS_ECN_ECT0) |
526 | | static let IPTOS_ECN_ECT1: CInt = CInt(CNIOLinux.IPTOS_ECN_ECT1) |
527 | | static let IPTOS_ECN_CE: CInt = CInt(CNIOLinux.IPTOS_ECN_CE) |
528 | | #elseif os(OpenBSD) |
529 | | static let IPTOS_ECN_NOTECT: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_NOTECT) |
530 | | static let IPTOS_ECN_MASK: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_MASK) |
531 | | static let IPTOS_ECN_ECT0: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_ECT0) |
532 | | static let IPTOS_ECN_ECT1: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_ECT1) |
533 | | static let IPTOS_ECN_CE: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_CE) |
534 | | #elseif os(Windows) |
535 | | static let IPTOS_ECN_NOTECT: CInt = CInt(0x00) |
536 | | static let IPTOS_ECN_MASK: CInt = CInt(0x03) |
537 | | static let IPTOS_ECN_ECT0: CInt = CInt(0x02) |
538 | | static let IPTOS_ECN_ECT1: CInt = CInt(0x01) |
539 | | static let IPTOS_ECN_CE: CInt = CInt(0x03) |
540 | | #endif |
541 | | |
542 | | #if canImport(Darwin) |
543 | | static let IP_RECVPKTINFO: CInt = CNIODarwin.IP_RECVPKTINFO |
544 | | static let IP_PKTINFO: CInt = CNIODarwin.IP_PKTINFO |
545 | | |
546 | | static let IPV6_RECVPKTINFO: CInt = CNIODarwin_IPV6_RECVPKTINFO |
547 | | static let IPV6_PKTINFO: CInt = CNIODarwin_IPV6_PKTINFO |
548 | | #elseif os(Linux) || os(FreeBSD) || os(Android) |
549 | | static let IP_RECVPKTINFO: CInt = CInt(CNIOLinux.IP_PKTINFO) |
550 | | static let IP_PKTINFO: CInt = CInt(CNIOLinux.IP_PKTINFO) |
551 | | |
552 | | static let IPV6_RECVPKTINFO: CInt = CInt(CNIOLinux.IPV6_RECVPKTINFO) |
553 | | static let IPV6_PKTINFO: CInt = CInt(CNIOLinux.IPV6_PKTINFO) |
554 | | #elseif os(OpenBSD) |
555 | | static let IP_PKTINFO: CInt = CInt(-1) // Not actually present. |
556 | | |
557 | | static let IPV6_RECVPKTINFO: CInt = CInt(CNIOOpenBSD.IPV6_RECVPKTINFO) |
558 | | static let IPV6_PKTINFO: CInt = CInt(CNIOOpenBSD.IPV6_PKTINFO) |
559 | | #elseif os(Windows) |
560 | | static let IP_RECVPKTINFO: CInt = CInt(WinSDK.IP_PKTINFO) |
561 | | static let IP_PKTINFO: CInt = CInt(WinSDK.IP_PKTINFO) |
562 | | |
563 | | static let IPV6_RECVPKTINFO: CInt = CInt(WinSDK.IPV6_PKTINFO) |
564 | | static let IPV6_PKTINFO: CInt = CInt(WinSDK.IPV6_PKTINFO) |
565 | | #endif |
566 | | |
567 | | #if canImport(Darwin) |
568 | | static let SOL_UDP: CInt = CInt(IPPROTO_UDP) |
569 | | #elseif os(Linux) || os(FreeBSD) || os(Android) || os(OpenBSD) |
570 | | static let SOL_UDP: CInt = CInt(IPPROTO_UDP) |
571 | | #elseif os(Windows) |
572 | | static let SOL_UDP: CInt = CInt(IPPROTO_UDP) |
573 | | #endif |
574 | | |
575 | | #if !os(Windows) |
576 | | @inline(never) |
577 | 0 | public static func shutdown(descriptor: CInt, how: Shutdown) throws { |
578 | 0 | _ = try syscall(blocking: false) { |
579 | 0 | sysShutdown(descriptor, how.cValue) |
580 | 0 | } |
581 | 0 | } |
582 | | |
583 | | @inline(never) |
584 | 0 | public static func close(descriptor: CInt) throws { |
585 | 0 | let res = sysClose(descriptor) |
586 | 0 | if res == -1 { |
587 | | #if os(Windows) |
588 | | var err: CInt = 0 |
589 | | _get_errno(&err) |
590 | | #else |
591 | 0 | let err = errno |
592 | | #endif |
593 | 0 |
|
594 | 0 | // There is really nothing "good" we can do when EINTR was reported on close. |
595 | 0 | // So just ignore it and "assume" everything is fine == we closed the file descriptor. |
596 | 0 | // |
597 | 0 | // For more details see: |
598 | 0 | // - https://bugs.chromium.org/p/chromium/issues/detail?id=269623 |
599 | 0 | // - https://lwn.net/Articles/576478/ |
600 | 0 | if err != EINTR { |
601 | 0 | preconditionIsNotUnacceptableErrnoOnClose(err: err, where: #function) |
602 | 0 | throw IOError(errnoCode: err, reason: "close") |
603 | 0 | } |
604 | 0 | } |
605 | 0 | } |
606 | | |
607 | | @inline(never) |
608 | 0 | public static func bind(descriptor: CInt, ptr: UnsafePointer<sockaddr>, bytes: Int) throws { |
609 | 0 | _ = try syscall(blocking: false) { |
610 | 0 | sysBind(descriptor, ptr, socklen_t(bytes)) |
611 | 0 | } |
612 | 0 | } |
613 | | |
614 | | @inline(never) |
615 | | @discardableResult |
616 | | @usableFromInline |
617 | | // TODO: Allow varargs |
618 | 0 | internal static func fcntl(descriptor: CInt, command: CInt, value: CInt) throws -> CInt { |
619 | 0 | try syscall(blocking: false) { |
620 | 0 | sysFcntl(descriptor, command, value) |
621 | 0 | }.result |
622 | 0 | } |
623 | | |
624 | | @inline(never) |
625 | | public static func socket( |
626 | | domain: NIOBSDSocket.ProtocolFamily, |
627 | | type: NIOBSDSocket.SocketType, |
628 | | protocolSubtype: NIOBSDSocket.ProtocolSubtype |
629 | 0 | ) throws -> CInt { |
630 | 0 | try syscall(blocking: false) { |
631 | 0 | sysSocket(domain.rawValue, type.rawValue, protocolSubtype.rawValue) |
632 | 0 | }.result |
633 | 0 | } |
634 | | |
635 | | @inline(never) |
636 | | public static func setsockopt( |
637 | | socket: CInt, |
638 | | level: CInt, |
639 | | optionName: CInt, |
640 | | optionValue: UnsafeRawPointer, |
641 | | optionLen: socklen_t |
642 | 0 | ) throws { |
643 | 0 | _ = try syscall(blocking: false) { |
644 | 0 | sysSetsockopt(socket, level, optionName, optionValue, optionLen) |
645 | 0 | } |
646 | 0 | } |
647 | | |
648 | | @inline(never) |
649 | | public static func getsockopt( |
650 | | socket: CInt, |
651 | | level: CInt, |
652 | | optionName: CInt, |
653 | | optionValue: UnsafeMutableRawPointer, |
654 | | optionLen: UnsafeMutablePointer<socklen_t> |
655 | 0 | ) throws { |
656 | 0 | _ = try syscall(blocking: false) { |
657 | 0 | sysGetsockopt(socket, level, optionName, optionValue, optionLen) |
658 | 0 | }.result |
659 | 0 | } |
660 | | |
661 | | @inline(never) |
662 | 0 | public static func listen(descriptor: CInt, backlog: CInt) throws { |
663 | 0 | _ = try syscall(blocking: false) { |
664 | 0 | sysListen(descriptor, backlog) |
665 | 0 | } |
666 | 0 | } |
667 | | |
668 | | @inline(never) |
669 | | public static func accept( |
670 | | descriptor: CInt, |
671 | | addr: UnsafeMutablePointer<sockaddr>?, |
672 | | len: UnsafeMutablePointer<socklen_t>? |
673 | 0 | ) throws -> CInt? { |
674 | 0 | let result: IOResult<CInt> = try syscall(blocking: true) { |
675 | 0 | sysAccept(descriptor, addr, len) |
676 | 0 | } |
677 | 0 |
|
678 | 0 | if case .processed(let fd) = result { |
679 | 0 | return fd |
680 | 0 | } else { |
681 | 0 | return nil |
682 | 0 | } |
683 | 0 | } |
684 | | |
685 | | @inline(never) |
686 | 0 | public static func connect(descriptor: CInt, addr: UnsafePointer<sockaddr>, size: socklen_t) throws -> Bool { |
687 | 0 | do { |
688 | 0 | _ = try syscall(blocking: false) { |
689 | 0 | sysConnect(descriptor, addr, size) |
690 | 0 | } |
691 | 0 | return true |
692 | 0 | } catch let err as IOError { |
693 | 0 | if err.errnoCode == EINPROGRESS { |
694 | 0 | return false |
695 | 0 | } |
696 | 0 | throw err |
697 | 0 | } |
698 | 0 | } |
699 | | |
700 | | @inline(never) |
701 | 0 | public static func open(file: UnsafePointer<CChar>, oFlag: CInt, mode: mode_t) throws -> CInt { |
702 | 0 | try syscall(blocking: false) { |
703 | 0 | sysOpenWithMode(file, oFlag, mode) |
704 | 0 | }.result |
705 | 0 | } |
706 | | |
707 | | @inline(never) |
708 | 0 | public static func open(file: UnsafePointer<CChar>, oFlag: CInt) throws -> CInt { |
709 | 0 | try syscall(blocking: false) { |
710 | 0 | sysOpen(file, oFlag) |
711 | 0 | }.result |
712 | 0 | } |
713 | | |
714 | | @inline(never) |
715 | | public static func pwrite( |
716 | | descriptor: CInt, |
717 | | pointer: UnsafeRawPointer, |
718 | | size: Int, |
719 | | offset: off_t |
720 | 0 | ) throws -> IOResult<Int> { |
721 | 0 | try syscall(blocking: true) { |
722 | 0 | sysPwrite(descriptor, pointer, size, offset) |
723 | 0 | } |
724 | 0 | } |
725 | | |
726 | | @inline(never) |
727 | 0 | public static func writev(descriptor: CInt, iovecs: UnsafeBufferPointer<IOVector>) throws -> IOResult<Int> { |
728 | 0 | try syscall(blocking: true) { |
729 | 0 | sysWritev(descriptor, iovecs.baseAddress!, CInt(iovecs.count)) |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | | @inline(never) |
734 | | public static func pread( |
735 | | descriptor: CInt, |
736 | | pointer: UnsafeMutableRawPointer, |
737 | | size: size_t, |
738 | | offset: off_t |
739 | 0 | ) throws -> IOResult<ssize_t> { |
740 | 0 | try syscallForbiddingEINVAL { |
741 | 0 | sysPread(descriptor, pointer, size, offset) |
742 | 0 | } |
743 | 0 | } |
744 | | |
745 | | @inline(never) |
746 | | public static func recvmsg( |
747 | | descriptor: CInt, |
748 | | msgHdr: UnsafeMutablePointer<msghdr>, |
749 | | flags: CInt |
750 | 0 | ) throws -> IOResult<ssize_t> { |
751 | 0 | try syscall(blocking: true) { |
752 | 0 | sysRecvMsg(descriptor, msgHdr, flags) |
753 | 0 | } |
754 | 0 | } |
755 | | |
756 | | @inline(never) |
757 | | public static func sendmsg( |
758 | | descriptor: CInt, |
759 | | msgHdr: UnsafePointer<msghdr>, |
760 | | flags: CInt |
761 | 0 | ) throws -> IOResult<ssize_t> { |
762 | 0 | try syscall(blocking: true) { |
763 | 0 | sysSendMsg(descriptor, msgHdr, flags) |
764 | 0 | } |
765 | 0 | } |
766 | | #endif |
767 | | |
768 | | @inline(never) |
769 | | public static func read( |
770 | | descriptor: CInt, |
771 | | pointer: UnsafeMutableRawPointer, |
772 | | size: size_t |
773 | 0 | ) throws -> IOResult<ssize_t> { |
774 | 0 | try syscallForbiddingEINVAL { |
775 | | #if os(Windows) |
776 | | // Windows read, reads at most UInt32. Lets clamp size there. |
777 | | let size = UInt32(clamping: size) |
778 | | return ssize_t(sysRead(descriptor, pointer, size)) |
779 | | #else |
780 | 0 | sysRead(descriptor, pointer, size) |
781 | | #endif |
782 | 0 | } |
783 | 0 | } |
784 | | |
785 | | @inline(never) |
786 | 0 | public static func write(descriptor: CInt, pointer: UnsafeRawPointer, size: Int) throws -> IOResult<Int> { |
787 | 0 | try syscall(blocking: true) { |
788 | | #if os(Windows) |
789 | | let size = UInt32(clamping: size) |
790 | | #endif |
791 | 0 | return numericCast(sysWrite(descriptor, pointer, size)) |
792 | 0 | } |
793 | 0 | } |
794 | | |
795 | | @discardableResult |
796 | | @inline(never) |
797 | 0 | public static func ftruncate(descriptor: CInt, size: off_t) throws -> CInt { |
798 | 0 | try syscall(blocking: false) { |
799 | 0 | sysFtruncate(descriptor, numericCast(size)) |
800 | 0 | }.result |
801 | 0 | } |
802 | | |
803 | | @discardableResult |
804 | | @inline(never) |
805 | 0 | public static func lseek(descriptor: CInt, offset: off_t, whence: CInt) throws -> off_t { |
806 | 0 | try syscall(blocking: false) { |
807 | 0 | sysLseek(descriptor, offset, whence) |
808 | 0 | }.result |
809 | 0 | } |
810 | | |
811 | | @discardableResult |
812 | | @inline(never) |
813 | 0 | public static func dup(descriptor: CInt) throws -> CInt { |
814 | 0 | try syscall(blocking: false) { |
815 | 0 | sysDup(descriptor) |
816 | 0 | }.result |
817 | 0 | } |
818 | | |
819 | | #if !os(Windows) |
820 | | // It's not really posix but exists on Linux and MacOS / BSD so just put it here for now to keep it simple |
821 | | @inline(never) |
822 | 0 | public static func sendfile(descriptor: CInt, fd: CInt, offset: off_t, count: size_t) throws -> IOResult<Int> { |
823 | 0 | var written: off_t = 0 |
824 | 0 | do { |
825 | 0 | _ = try syscall(blocking: false) { () -> ssize_t in |
826 | | #if canImport(Darwin) |
827 | | var w: off_t = off_t(count) |
828 | | let result: CInt = Darwin.sendfile(fd, descriptor, offset, &w, nil, 0) |
829 | | written = w |
830 | | return ssize_t(result) |
831 | | #elseif os(Linux) || os(FreeBSD) || os(Android) |
832 | 0 | var off: off_t = offset |
833 | | #if canImport(Glibc) |
834 | 0 | let result: ssize_t = Glibc.sendfile(descriptor, fd, &off, count) |
835 | | #elseif canImport(Musl) |
836 | | let result: ssize_t = Musl.sendfile(descriptor, fd, &off, count) |
837 | | #elseif canImport(Android) |
838 | | let result: ssize_t = Android.sendfile(descriptor, fd, &off, count) |
839 | | #endif |
840 | 0 | if result >= 0 { |
841 | 0 | written = off_t(result) |
842 | 0 | } else { |
843 | 0 | written = 0 |
844 | 0 | } |
845 | 0 | return result |
846 | | #else |
847 | | fatalError("unsupported OS") |
848 | | #endif |
849 | 0 | } |
850 | 0 | return .processed(Int(written)) |
851 | 0 | } catch let err as IOError { |
852 | 0 | if err.errnoCode == EAGAIN { |
853 | 0 | return .wouldBlock(Int(written)) |
854 | 0 | } |
855 | 0 | throw err |
856 | 0 | } |
857 | 0 | } |
858 | | |
859 | | @inline(never) |
860 | | public static func sendmmsg( |
861 | | sockfd: CInt, |
862 | | msgvec: UnsafeMutablePointer<MMsgHdr>, |
863 | | vlen: CUnsignedInt, |
864 | | flags: CInt |
865 | 0 | ) throws -> IOResult<Int> { |
866 | 0 | try syscall(blocking: true) { |
867 | 0 | Int(sysSendMmsg(sockfd, msgvec, vlen, flags)) |
868 | 0 | } |
869 | 0 | } |
870 | | |
871 | | @inline(never) |
872 | | public static func recvmmsg( |
873 | | sockfd: CInt, |
874 | | msgvec: UnsafeMutablePointer<MMsgHdr>, |
875 | | vlen: CUnsignedInt, |
876 | | flags: CInt, |
877 | | timeout: UnsafeMutablePointer<timespec>? |
878 | 0 | ) throws -> IOResult<Int> { |
879 | 0 | try syscall(blocking: true) { |
880 | 0 | Int(sysRecvMmsg(sockfd, msgvec, vlen, flags, timeout)) |
881 | 0 | } |
882 | 0 | } |
883 | | |
884 | | @inline(never) |
885 | | public static func getpeername( |
886 | | socket: CInt, |
887 | | address: UnsafeMutablePointer<sockaddr>, |
888 | | addressLength: UnsafeMutablePointer<socklen_t> |
889 | 0 | ) throws { |
890 | 0 | _ = try syscall(blocking: false) { |
891 | 0 | sysGetpeername(socket, address, addressLength) |
892 | 0 | } |
893 | 0 | } |
894 | | |
895 | | @inline(never) |
896 | | public static func getsockname( |
897 | | socket: CInt, |
898 | | address: UnsafeMutablePointer<sockaddr>, |
899 | | addressLength: UnsafeMutablePointer<socklen_t> |
900 | 0 | ) throws { |
901 | 0 | _ = try syscall(blocking: false) { |
902 | 0 | sysGetsockname(socket, address, addressLength) |
903 | 0 | } |
904 | 0 | } |
905 | | #endif |
906 | | |
907 | | @inline(never) |
908 | 0 | public static func if_nametoindex(_ name: UnsafePointer<CChar>?) throws -> CUnsignedInt { |
909 | 0 | try syscall(blocking: false) { |
910 | 0 | sysIfNameToIndex(name!) |
911 | 0 | }.result |
912 | 0 | } |
913 | | |
914 | | #if !os(Windows) |
915 | | @inline(never) |
916 | 0 | public static func poll(fds: UnsafeMutablePointer<pollfd>, nfds: nfds_t, timeout: CInt) throws -> CInt { |
917 | 0 | try syscall(blocking: false) { |
918 | 0 | sysPoll(fds, nfds, timeout) |
919 | 0 | }.result |
920 | 0 | } |
921 | | |
922 | | @inline(never) |
923 | 0 | public static func fstat(descriptor: CInt, outStat: UnsafeMutablePointer<stat>) throws { |
924 | 0 | _ = try syscall(blocking: false) { |
925 | 0 | sysFstat(descriptor, outStat) |
926 | 0 | } |
927 | 0 | } |
928 | | |
929 | | @inline(never) |
930 | 0 | public static func stat(pathname: String, outStat: UnsafeMutablePointer<stat>) throws { |
931 | 0 | _ = try syscall(blocking: false) { |
932 | 0 | sysStat(pathname, outStat) |
933 | 0 | } |
934 | 0 | } |
935 | | |
936 | | @inline(never) |
937 | 0 | public static func lstat(pathname: String, outStat: UnsafeMutablePointer<stat>) throws { |
938 | 0 | _ = try syscall(blocking: false) { |
939 | 0 | sysLstat(pathname, outStat) |
940 | 0 | } |
941 | 0 | } |
942 | | |
943 | | @inline(never) |
944 | 0 | public static func symlink(pathname: String, destination: String) throws { |
945 | 0 | _ = try syscall(blocking: false) { |
946 | 0 | sysSymlink(destination, pathname) |
947 | 0 | } |
948 | 0 | } |
949 | | |
950 | | @inline(never) |
951 | | public static func readlink( |
952 | | pathname: String, |
953 | | outPath: UnsafeMutablePointer<CChar>, |
954 | | outPathSize: Int |
955 | 0 | ) throws -> CLong { |
956 | 0 | try syscall(blocking: false) { |
957 | 0 | sysReadlink(pathname, outPath, outPathSize) |
958 | 0 | }.result |
959 | 0 | } |
960 | | |
961 | | @inline(never) |
962 | 0 | public static func unlink(pathname: String) throws { |
963 | 0 | _ = try syscall(blocking: false) { |
964 | 0 | sysUnlink(pathname) |
965 | 0 | } |
966 | 0 | } |
967 | | |
968 | | @inline(never) |
969 | 0 | public static func mkdir(pathname: String, mode: mode_t) throws { |
970 | 0 | _ = try syscall(blocking: false) { |
971 | 0 | sysMkdir(pathname, mode) |
972 | 0 | } |
973 | 0 | } |
974 | | |
975 | | #if canImport(Darwin) |
976 | | @inline(never) |
977 | | public static func mkpath_np(pathname: String, mode: mode_t) throws { |
978 | | _ = try syscall(blocking: false) { |
979 | | sysMkpath(pathname, mode) |
980 | | } |
981 | | } |
982 | | |
983 | | @inline(never) |
984 | | public static func opendir(pathname: String) throws -> UnsafeMutablePointer<DIR> { |
985 | | try syscall { |
986 | | sysOpendir(pathname) |
987 | | } |
988 | | } |
989 | | |
990 | | @inline(never) |
991 | | public static func readdir(dir: UnsafeMutablePointer<DIR>) throws -> UnsafeMutablePointer<dirent>? { |
992 | | try syscallOptional { |
993 | | sysReaddir(dir) |
994 | | } |
995 | | } |
996 | | |
997 | | @inline(never) |
998 | | public static func closedir(dir: UnsafeMutablePointer<DIR>) throws { |
999 | | _ = try syscall(blocking: true) { |
1000 | | sysClosedir(dir) |
1001 | | } |
1002 | | } |
1003 | | #elseif os(Linux) || os(FreeBSD) || os(Android) || os(OpenBSD) |
1004 | | @inline(never) |
1005 | 0 | public static func opendir(pathname: String) throws -> OpaquePointer { |
1006 | 0 | try syscall { |
1007 | 0 | sysOpendir(pathname) |
1008 | 0 | } |
1009 | 0 | } |
1010 | | |
1011 | | @inline(never) |
1012 | 0 | public static func readdir(dir: OpaquePointer) throws -> UnsafeMutablePointer<dirent>? { |
1013 | 0 | try syscallOptional { |
1014 | 0 | sysReaddir(dir) |
1015 | 0 | } |
1016 | 0 | } |
1017 | | |
1018 | | @inline(never) |
1019 | 0 | public static func closedir(dir: OpaquePointer) throws { |
1020 | 0 | _ = try syscall(blocking: true) { |
1021 | 0 | sysClosedir(dir) |
1022 | 0 | } |
1023 | 0 | } |
1024 | | #endif |
1025 | | |
1026 | | @inline(never) |
1027 | 0 | public static func rename(pathname: String, newName: String) throws { |
1028 | 0 | _ = try syscall(blocking: true) { |
1029 | 0 | sysRename(pathname, newName) |
1030 | 0 | } |
1031 | 0 | } |
1032 | | |
1033 | | @inline(never) |
1034 | 0 | public static func remove(pathname: String) throws { |
1035 | 0 | _ = try syscall(blocking: true) { |
1036 | 0 | sysRemove(pathname) |
1037 | 0 | } |
1038 | 0 | } |
1039 | | |
1040 | | @inline(never) |
1041 | | public static func socketpair( |
1042 | | domain: NIOBSDSocket.ProtocolFamily, |
1043 | | type: NIOBSDSocket.SocketType, |
1044 | | protocolSubtype: NIOBSDSocket.ProtocolSubtype, |
1045 | | socketVector: UnsafeMutablePointer<CInt>? |
1046 | 0 | ) throws { |
1047 | 0 | _ = try syscall(blocking: false) { |
1048 | 0 | sysSocketpair(domain.rawValue, type.rawValue, protocolSubtype.rawValue, socketVector!) |
1049 | 0 | } |
1050 | 0 | } |
1051 | | #endif |
1052 | | #if !os(Windows) |
1053 | | @inline(never) |
1054 | 0 | public static func ioctl(fd: CInt, request: CUnsignedLong, ptr: UnsafeMutableRawPointer) throws { |
1055 | 0 | _ = try syscall(blocking: false) { |
1056 | 0 | /// `numericCast` to support musl which accepts `CInt` (cf. `CUnsignedLong`). |
1057 | 0 | sysIoctl(fd, numericCast(request), ptr) |
1058 | 0 | } |
1059 | 0 | } |
1060 | | #endif // !os(Windows) |
1061 | | } |
1062 | | |
1063 | | /// `NIOFcntlFailedError` indicates that NIO was unable to perform an |
1064 | | /// operation on a socket. |
1065 | | /// |
1066 | | /// This error should never happen, unfortunately, we have seen this happen on Darwin. |
1067 | | public struct NIOFcntlFailedError: Error {} |
1068 | | |
1069 | | /// `NIOFailedToSetSocketNonBlockingError` indicates that NIO was unable to set a socket to non-blocking mode, either |
1070 | | /// when connecting a socket as a client or when accepting a socket as a server. |
1071 | | /// |
1072 | | /// This error should never happen because a socket should always be able to be set to non-blocking mode. Unfortunately, |
1073 | | /// we have seen this happen on Darwin. |
1074 | | @available(*, deprecated, renamed: "NIOFcntlFailedError") |
1075 | | public struct NIOFailedToSetSocketNonBlockingError: Error {} |
1076 | | |
1077 | | #if !os(Windows) |
1078 | | extension Posix { |
1079 | 0 | public static func setNonBlocking(socket: CInt) throws { |
1080 | 0 | let flags = try Posix.fcntl(descriptor: socket, command: F_GETFL, value: 0) |
1081 | 0 | do { |
1082 | 0 | let ret = try Posix.fcntl(descriptor: socket, command: F_SETFL, value: flags | O_NONBLOCK) |
1083 | 0 | assert(ret == 0, "unexpectedly, fcntl(\(socket), F_SETFL, \(flags) | O_NONBLOCK) returned \(ret)") |
1084 | 0 | } catch let error as IOError { |
1085 | 0 | if error.errnoCode == EINVAL { |
1086 | 0 | // Darwin seems to sometimes do this despite the docs claiming it can't happen |
1087 | 0 | throw NIOFcntlFailedError() |
1088 | 0 | } |
1089 | 0 | throw error |
1090 | 0 | } |
1091 | 0 | } |
1092 | | } |
1093 | | #endif |
1094 | | |
1095 | | #if canImport(Darwin) || os(OpenBSD) |
1096 | | #if canImport(Darwin) |
1097 | | internal typealias kevent_timespec = Darwin.timespec |
1098 | | #elseif os(OpenBSD) |
1099 | | internal typealias kevent_timespec = CNIOOpenBSD.timespec |
1100 | | #else |
1101 | | #error("implementation missing") |
1102 | | #endif |
1103 | | |
1104 | | @usableFromInline |
1105 | | internal enum KQueue: Sendable { |
1106 | | |
1107 | | // TODO: Figure out how to specify a typealias to the kevent struct without run into trouble with the swift compiler |
1108 | | |
1109 | | @inline(never) |
1110 | | public static func kqueue() throws -> CInt { |
1111 | | try syscall(blocking: false) { |
1112 | | #if canImport(Darwin) |
1113 | | Darwin.kqueue() |
1114 | | #elseif os(OpenBSD) |
1115 | | CNIOOpenBSD.kqueue() |
1116 | | #else |
1117 | | #error("implementation missing") |
1118 | | #endif |
1119 | | }.result |
1120 | | } |
1121 | | |
1122 | | @inline(never) |
1123 | | @discardableResult |
1124 | | public static func kevent( |
1125 | | kq: CInt, |
1126 | | changelist: UnsafePointer<kevent>?, |
1127 | | nchanges: CInt, |
1128 | | eventlist: UnsafeMutablePointer<kevent>?, |
1129 | | nevents: CInt, |
1130 | | timeout: UnsafePointer<kevent_timespec>? |
1131 | | ) throws -> CInt { |
1132 | | try syscall(blocking: false) { |
1133 | | sysKevent(kq, changelist, nchanges, eventlist, nevents, timeout) |
1134 | | }.result |
1135 | | } |
1136 | | } |
1137 | | #endif |
1138 | | #endif // !os(WASI) |