Commit 79313d844f

Brandon Black <bblack@wikimedia.org>
2025-09-08 20:33:00
socketpair: posix wrapper, void on windows
socketpair is something like a pipe2() for sockets, and generally only works for AF_UNIX sockets for most platforms. Winsock2 explicitly does not support this call, even though it does have AF_UNIX sockets.
1 parent 05cff8a
Changed files (2)
lib/std/c.zig
@@ -10599,6 +10599,12 @@ pub const socket = switch (native_os) {
     else => private.socket,
 };
 
+pub const socketpair = switch (native_os) {
+    // https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/#unsupported\unavailable:
+    .windows => void,
+    else => private.socketpair,
+};
+
 pub const stat = switch (native_os) {
     .macos => switch (native_arch) {
         .x86_64 => private.@"stat$INODE64",
@@ -10740,7 +10746,6 @@ pub extern "c" fn uname(buf: *utsname) c_int;
 pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int;
 pub extern "c" fn shutdown(socket: fd_t, how: c_int) c_int;
 pub extern "c" fn bind(socket: fd_t, address: ?*const sockaddr, address_len: socklen_t) c_int;
-pub extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint, sv: *[2]fd_t) c_int;
 pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int;
 pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int;
 pub extern "c" fn getpeername(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int;
@@ -11429,6 +11434,7 @@ const private = struct {
     extern "c" fn sigismember(set: ?*const sigset_t, signo: c_int) c_int;
     extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
     extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
+    extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint, sv: *[2]fd_t) c_int;
     extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;
     extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
     extern "c" fn sysconf(sc: c_int) c_long;
lib/std/posix.zig
@@ -3671,6 +3671,46 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t
     }
 }
 
+pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]socket_t {
+    // Note to the future: we could provide a shim here for e.g. windows which
+    // creates a listening socket, then creates a second socket and connects it
+    // to the listening socket, and then returns the two.
+    if (@TypeOf(system.socketpair) == void)
+        @compileError("socketpair() not supported by this OS");
+
+    // I'm not really sure if haiku supports flags here.  I'm following the
+    // existing filter here from pipe2(), because it sure seems like it
+    // supports flags there too, but haiku can be hard to understand.
+    const have_sock_flags = !builtin.target.os.tag.isDarwin() and native_os != .haiku;
+    const filtered_sock_type = if (!have_sock_flags)
+        socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC)
+    else
+        socket_type;
+    var socks: [2]socket_t = undefined;
+    const rc = system.socketpair(domain, filtered_sock_type, protocol, &socks);
+    switch (errno(rc)) {
+        .SUCCESS => {
+            errdefer close(socks[0]);
+            errdefer close(socks[1]);
+            if (!have_sock_flags) {
+                try setSockFlags(socks[0], socket_type);
+                try setSockFlags(socks[1], socket_type);
+            }
+            return socks;
+        },
+        .ACCES => return error.AccessDenied,
+        .AFNOSUPPORT => return error.AddressFamilyNotSupported,
+        .INVAL => return error.ProtocolFamilyNotAvailable,
+        .MFILE => return error.ProcessFdQuotaExceeded,
+        .NFILE => return error.SystemFdQuotaExceeded,
+        .NOBUFS => return error.SystemResources,
+        .NOMEM => return error.SystemResources,
+        .PROTONOSUPPORT => return error.ProtocolNotSupported,
+        .PROTOTYPE => return error.SocketTypeNotSupported,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
 pub const ShutdownError = error{
     ConnectionAborted,