commit f6edab32b290c79e655ead598ed8bb7ffe93f90b
parent 11bf1261ae1afc556cd3b65c3ddf52730cdee20b
Author: Noah Pederson <noah@packetlost.dev>
Date: Wed, 4 May 2022 17:03:28 -0500
Add CLOEXEC, NONBLOCK flags to net::accept
Creates an enum to wrap the underlying OS flags values from rt and limit
to the allowed subset by defining a new enum type called sock_flags.
Adds a rt::accept4 method which mirrors the accept(2) from
<sys/socket.h> and switches calls to net::accept to use that. Given that
flags should default to 0 and accept4 is documented to behave
identically to accept when flags are 0, there is no reason to support
both rt::accept and rt::accept4 outside of internal api cleanliness.
Updates both FreeBSD and Linux net:: and rt:: implementations.
Signed-off-by: Noah Pederson <noah@packetlost.dev>
Diffstat:
6 files changed, 56 insertions(+), 8 deletions(-)
diff --git a/net/+freebsd.ha b/net/+freebsd.ha
@@ -1,6 +1,7 @@
// License: MPL-2.0
// (c) 2021 Drew DeVault <sir@cmpwn.com>
// (c) 2021 Eyal Sawady <ecs@d2evs.net>
+// (c) 2022 Noah Pederson <noah@packetlost.dev>
use errors;
use fmt;
use io;
@@ -9,12 +10,30 @@ use os;
use rt;
use strings;
+// Optional flags to [[accept]] to be set on the returned [[io::file]].
+// See the O_CLOEXEC and O_NONBLOCK sections of open(2) for details.
+export type sockflags = enum int {
+ CLOEXEC = rt::SOCK_CLOEXEC,
+ NONBLOCK = rt::SOCK_NONBLOCK
+};
+
// Accepts the next connection from a socket. Blocks until a new connection is
-// available.
-export fn accept(sock: io::file) (io::file | error) = {
+// available. Optionally accepts CLOEXEC and NONBLOCK flags. If flags are
+// supplied, the [[io::file]] returned will have the supplied flags set.
+// If no flags are supplied, CLOEXEC is used by default.
+export fn accept(sock: io::file, flags: sockflags...) (io::file | error) = {
let sn = rt::sockaddr {...};
const sz = size(rt::sockaddr): u32;
- const fd = match (rt::accept(sock, &sn, &sz)) {
+ // Default to CLOEXEC if no flags are supplied
+ if (len(flags) == 0) {
+ flags = [sockflags::CLOEXEC];
+ };
+ // Apply any supplied flags
+ let f = 0i;
+ for (let i = 0z; i < len(flags); i += 1) {
+ f |= flags[i];
+ };
+ const fd = match (rt::accept4(sock, &sn, &sz, f)) {
case let err: rt::errno =>
return errors::errno(err);
case let fd: int =>
diff --git a/net/+linux.ha b/net/+linux.ha
@@ -1,6 +1,7 @@
// License: MPL-2.0
// (c) 2021 Drew DeVault <sir@cmpwn.com>
// (c) 2021 Eyal Sawady <ecs@d2evs.net>
+// (c) 2022 Noah Pederson <noah@packetlost.dev>
use errors;
use fmt;
use io;
@@ -9,12 +10,30 @@ use os;
use rt;
use strings;
+// Optional flags to [[accept]] to be set on the returned [[io::file]].
+// See the O_CLOEXEC and O_NONBLOCK sections of open(2) for details.
+export type sockflags = enum int {
+ CLOEXEC = rt::SOCK_CLOEXEC,
+ NONBLOCK = rt::SOCK_NONBLOCK
+};
+
// Accepts the next connection from a socket. Blocks until a new connection is
-// available.
-export fn accept(sock: io::file) (io::file | error) = {
+// available. Optionally accepts CLOEXEC and NONBLOCK flags. If flags are
+// supplied, the [[io::file]] returned will have the supplied flags set.
+// If no flags are supplied, CLOEXEC is used by default.
+export fn accept(sock: io::file, flags: sockflags...) (io::file | error) = {
let sn = rt::sockaddr {...};
const sz = size(rt::sockaddr): u32;
- const fd = match (rt::accept(sock, &sn, &sz)) {
+ // Default to CLOEXEC if no flags are supplied
+ if (len(flags) == 0) {
+ flags = [sockflags::CLOEXEC];
+ };
+ // Apply any supplied flags
+ let f = 0i;
+ for (let i = 0z; i < len(flags); i += 1) {
+ f |= flags[i];
+ };
+ const fd = match (rt::accept4(sock, &sn, &sz, f: int)) {
case let err: rt::errno =>
return errors::errno(err);
case let fd: int =>
diff --git a/net/tcp/listener.ha b/net/tcp/listener.ha
@@ -5,7 +5,7 @@ use net;
// Accepts the next connection from a socket. Blocks until a new connection is
// available. This is a convenience wrapper around [[net::accept]].
-export fn accept(sock: io::file) (io::file | net::error) = net::accept(sock);
+export fn accept(sock: io::file, flags: net::sockflags...) (io::file | net::error) = net::accept(sock, flags...);
// Shuts down a listening socket. This is a convenience wrapper around
// [[net::shutdown]].
diff --git a/net/unix/listener.ha b/net/unix/listener.ha
@@ -5,7 +5,7 @@ use net;
// Accepts the next connection from a socket. Blocks until a new connection is
// available. This is a convenience wrapper around [[net::accept]].
-export fn accept(sock: io::file) (io::file | net::error) = net::accept(sock);
+export fn accept(sock: io::file, flags: net::sockflags...) (io::file | net::error) = net::accept(sock, flags...);
// Shuts down a listening socket. This is a convenience wrapper around
// [[net::shutdown]].
diff --git a/rt/+freebsd/syscalls.ha b/rt/+freebsd/syscalls.ha
@@ -421,6 +421,11 @@ export fn accept(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32)
sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64))?: int;
};
+export fn accept4(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32, flags: int) (int | errno) = {
+ return wrap_return(syscall4(SYS_accept,
+ sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64, flags: u64))?: int;
+};
+
export fn recvfrom(sockfd: int, buf: *void, len_: size, flags: int,
src_addr: nullable *sockaddr, addrlen: nullable *u32
) (size | errno) = {
diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha
@@ -583,6 +583,11 @@ export fn accept(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32)
sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64))?: int;
};
+export fn accept4(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32, flags: int) (int | errno) = {
+ return wrap_return(syscall4(SYS_accept,
+ sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64, flags: u64))?: int;
+};
+
export fn recvfrom(sockfd: int, buf: *void, len_: size, flags: int,
src_addr: nullable *sockaddr, addrlen: nullable *u32
) (size | errno) = {