mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
Merge pull request 'add std.Io.net.Socket.createPair + handful of std.posix removals' (#31056) from std.posix-removals into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31056
This commit is contained in:
@@ -688,6 +688,7 @@ pub const VTable = struct {
|
||||
netConnectIp: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ConnectOptions) net.IpAddress.ConnectError!net.Stream,
|
||||
netListenUnix: *const fn (?*anyopaque, *const net.UnixAddress, net.UnixAddress.ListenOptions) net.UnixAddress.ListenError!net.Socket.Handle,
|
||||
netConnectUnix: *const fn (?*anyopaque, *const net.UnixAddress) net.UnixAddress.ConnectError!net.Socket.Handle,
|
||||
netSocketCreatePair: *const fn (?*anyopaque, net.Socket.CreatePairOptions) net.Socket.CreatePairError![2]net.Socket,
|
||||
netSend: *const fn (?*anyopaque, net.Socket.Handle, []net.OutgoingMessage, net.SendFlags) struct { ?net.Socket.SendError, usize },
|
||||
netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize },
|
||||
/// Returns 0 on end of stream.
|
||||
|
||||
+127
-79
@@ -1684,6 +1684,7 @@ pub fn io(t: *Threaded) Io {
|
||||
.windows => netConnectUnixWindows,
|
||||
else => netConnectUnixPosix,
|
||||
},
|
||||
.netSocketCreatePair = netSocketCreatePair,
|
||||
.netClose = netClose,
|
||||
.netShutdown = switch (native_os) {
|
||||
.windows => netShutdownWindows,
|
||||
@@ -1824,6 +1825,7 @@ pub fn ioBasic(t: *Threaded) Io {
|
||||
.netAccept = netAcceptUnavailable,
|
||||
.netBindIp = netBindIpUnavailable,
|
||||
.netConnectIp = netConnectIpUnavailable,
|
||||
.netSocketCreatePair = netSocketCreatePairUnavailable,
|
||||
.netConnectUnix = netConnectUnixUnavailable,
|
||||
.netClose = netCloseUnavailable,
|
||||
.netShutdown = netShutdownUnavailable,
|
||||
@@ -10612,43 +10614,36 @@ fn posixConnect(
|
||||
addr_len: posix.socklen_t,
|
||||
) !void {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
switch (posix.errno(posix.system.connect(socket_fd, addr, addr_len))) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return;
|
||||
},
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |e| {
|
||||
syscall.finish();
|
||||
switch (e) {
|
||||
.ADDRNOTAVAIL => return error.AddressUnavailable,
|
||||
.AFNOSUPPORT => return error.AddressFamilyUnsupported,
|
||||
.AGAIN, .INPROGRESS => return error.WouldBlock,
|
||||
.ALREADY => return error.ConnectionPending,
|
||||
.BADF => |err| return errnoBug(err), // File descriptor used after closed.
|
||||
.CONNREFUSED => return error.ConnectionRefused,
|
||||
.CONNRESET => return error.ConnectionResetByPeer,
|
||||
.FAULT => |err| return errnoBug(err),
|
||||
.ISCONN => |err| return errnoBug(err),
|
||||
.HOSTUNREACH => return error.HostUnreachable,
|
||||
.NETUNREACH => return error.NetworkUnreachable,
|
||||
.NOTSOCK => |err| return errnoBug(err),
|
||||
.PROTOTYPE => |err| return errnoBug(err),
|
||||
.TIMEDOUT => return error.Timeout,
|
||||
.CONNABORTED => |err| return errnoBug(err),
|
||||
.ACCES => return error.AccessDenied,
|
||||
.PERM => |err| return errnoBug(err),
|
||||
.NOENT => |err| return errnoBug(err),
|
||||
.NETDOWN => return error.NetworkDown,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
while (true) switch (posix.errno(posix.system.connect(socket_fd, addr, addr_len))) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return;
|
||||
},
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
.ADDRNOTAVAIL => return syscall.fail(error.AddressUnavailable),
|
||||
.AFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
|
||||
.AGAIN, .INPROGRESS => return syscall.fail(error.WouldBlock),
|
||||
.ALREADY => return syscall.fail(error.ConnectionPending),
|
||||
.CONNREFUSED => return syscall.fail(error.ConnectionRefused),
|
||||
.CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
|
||||
.HOSTUNREACH => return syscall.fail(error.HostUnreachable),
|
||||
.NETUNREACH => return syscall.fail(error.NetworkUnreachable),
|
||||
.TIMEDOUT => return syscall.fail(error.Timeout),
|
||||
.ACCES => return syscall.fail(error.AccessDenied),
|
||||
.NETDOWN => return syscall.fail(error.NetworkDown),
|
||||
.BADF => |err| return syscall.errnoBug(err), // File descriptor used after closed.
|
||||
.CONNABORTED => |err| return syscall.errnoBug(err),
|
||||
.FAULT => |err| return syscall.errnoBug(err),
|
||||
.ISCONN => |err| return syscall.errnoBug(err),
|
||||
.NOENT => |err| return syscall.errnoBug(err),
|
||||
.NOTSOCK => |err| return syscall.errnoBug(err),
|
||||
.PERM => |err| return syscall.errnoBug(err),
|
||||
.PROTOTYPE => |err| return syscall.errnoBug(err),
|
||||
else => |err| return syscall.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
|
||||
fn posixConnectUnix(
|
||||
@@ -11106,46 +11101,31 @@ fn openSocketPosix(
|
||||
}!posix.socket_t {
|
||||
const mode = posixSocketMode(options.mode);
|
||||
const protocol = posixProtocol(options.protocol);
|
||||
const flags: u32 = mode | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC;
|
||||
const syscall: Syscall = try .start();
|
||||
const socket_fd = while (true) {
|
||||
const flags: u32 = mode | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC;
|
||||
const socket_rc = posix.system.socket(family, flags, protocol);
|
||||
switch (posix.errno(socket_rc)) {
|
||||
const rc = posix.system.socket(family, flags, protocol);
|
||||
switch (posix.errno(rc)) {
|
||||
.SUCCESS => {
|
||||
const fd: posix.fd_t = @intCast(socket_rc);
|
||||
errdefer posix.close(fd);
|
||||
if (socket_flags_unsupported) while (true) {
|
||||
try syscall.checkCancel();
|
||||
switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
|
||||
.SUCCESS => break,
|
||||
.INTR => continue,
|
||||
else => |err| {
|
||||
syscall.finish();
|
||||
return posix.unexpectedErrno(err);
|
||||
},
|
||||
}
|
||||
};
|
||||
syscall.finish();
|
||||
const fd: posix.fd_t = @intCast(rc);
|
||||
errdefer posix.close(fd);
|
||||
if (socket_flags_unsupported) try setCloexec(fd);
|
||||
break fd;
|
||||
},
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |e| {
|
||||
syscall.finish();
|
||||
switch (e) {
|
||||
.AFNOSUPPORT => return error.AddressFamilyUnsupported,
|
||||
.INVAL => return error.ProtocolUnsupportedBySystem,
|
||||
.MFILE => return error.ProcessFdQuotaExceeded,
|
||||
.NFILE => return error.SystemFdQuotaExceeded,
|
||||
.NOBUFS => return error.SystemResources,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.PROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily,
|
||||
.PROTOTYPE => return error.SocketModeUnsupported,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
},
|
||||
.AFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
|
||||
.INVAL => return syscall.fail(error.ProtocolUnsupportedBySystem),
|
||||
.MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
|
||||
.NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
|
||||
.NOBUFS => return syscall.fail(error.SystemResources),
|
||||
.NOMEM => return syscall.fail(error.SystemResources),
|
||||
.PROTONOSUPPORT => return syscall.fail(error.ProtocolUnsupportedByAddressFamily),
|
||||
.PROTOTYPE => return syscall.fail(error.SocketModeUnsupported),
|
||||
else => |err| return syscall.unexpectedErrno(err),
|
||||
}
|
||||
};
|
||||
errdefer posix.close(socket_fd);
|
||||
@@ -11158,6 +11138,84 @@ fn openSocketPosix(
|
||||
return socket_fd;
|
||||
}
|
||||
|
||||
fn setCloexec(fd: posix.fd_t) error{ Canceled, Unexpected }!void {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
|
||||
.SUCCESS => return syscall.finish(),
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |err| return syscall.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
|
||||
fn netSocketCreatePair(
|
||||
userdata: ?*anyopaque,
|
||||
options: net.Socket.CreatePairOptions,
|
||||
) net.Socket.CreatePairError![2]net.Socket {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
if (!have_networking) return error.OperationUnsupported;
|
||||
if (@TypeOf(posix.system.socketpair) == void) return error.OperationUnsupported;
|
||||
if (native_os == .haiku) @panic("TODO");
|
||||
|
||||
const family: posix.sa_family_t = switch (options.family) {
|
||||
.ip4 => posix.AF.INET,
|
||||
.ip6 => posix.AF.INET6,
|
||||
};
|
||||
const mode = posixSocketMode(options.mode);
|
||||
const protocol = posixProtocol(options.protocol);
|
||||
const flags: u32 = mode | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC;
|
||||
|
||||
var sockets: [2]posix.socket_t = undefined;
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (posix.errno(posix.system.socketpair(family, flags, protocol, &sockets))) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
errdefer {
|
||||
posix.close(sockets[0]);
|
||||
posix.close(sockets[1]);
|
||||
}
|
||||
if (socket_flags_unsupported) {
|
||||
try setCloexec(sockets[0]);
|
||||
try setCloexec(sockets[1]);
|
||||
}
|
||||
var storages: [2]PosixAddress = undefined;
|
||||
var addr_lens: [2]posix.socklen_t = .{ @sizeOf(PosixAddress), @sizeOf(PosixAddress) };
|
||||
try posixGetSockName(sockets[0], &storages[0].any, &addr_lens[0]);
|
||||
try posixGetSockName(sockets[1], &storages[1].any, &addr_lens[1]);
|
||||
return .{
|
||||
.{ .handle = sockets[0], .address = addressFromPosix(&storages[0]) },
|
||||
.{ .handle = sockets[1], .address = addressFromPosix(&storages[1]) },
|
||||
};
|
||||
},
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
.ACCES => return syscall.fail(error.AccessDenied),
|
||||
.AFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
|
||||
.INVAL => return syscall.fail(error.ProtocolUnsupportedBySystem),
|
||||
.MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
|
||||
.NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
|
||||
.NOBUFS => return syscall.fail(error.SystemResources),
|
||||
.NOMEM => return syscall.fail(error.SystemResources),
|
||||
.PROTONOSUPPORT => return syscall.fail(error.ProtocolUnsupportedByAddressFamily),
|
||||
.PROTOTYPE => return syscall.fail(error.SocketModeUnsupported),
|
||||
else => |err| return syscall.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
|
||||
fn netSocketCreatePairUnavailable(
|
||||
userdata: ?*anyopaque,
|
||||
options: net.Socket.CreatePairOptions,
|
||||
) net.Socket.CreatePairError![2]net.Socket {
|
||||
_ = userdata;
|
||||
_ = options;
|
||||
return error.OperationUnsupported;
|
||||
}
|
||||
|
||||
fn openSocketWsa(
|
||||
t: *Threaded,
|
||||
family: posix.sa_family_t,
|
||||
@@ -11216,20 +11274,10 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve
|
||||
posix.system.accept(listen_fd, &storage.any, &addr_len);
|
||||
switch (posix.errno(rc)) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
const fd: posix.fd_t = @intCast(rc);
|
||||
errdefer posix.close(fd);
|
||||
if (!have_accept4) while (true) {
|
||||
try syscall.checkCancel();
|
||||
switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
|
||||
.SUCCESS => break,
|
||||
.INTR => continue,
|
||||
else => |err| {
|
||||
syscall.finish();
|
||||
return posix.unexpectedErrno(err);
|
||||
},
|
||||
}
|
||||
};
|
||||
syscall.finish();
|
||||
if (!have_accept4) try setCloexec(fd);
|
||||
break fd;
|
||||
},
|
||||
.INTR => {
|
||||
|
||||
@@ -1187,6 +1187,35 @@ pub const Socket = struct {
|
||||
) struct { ?ReceiveTimeoutError, usize } {
|
||||
return io.vtable.netReceive(io.userdata, s.handle, message_buffer, data_buffer, flags, timeout);
|
||||
}
|
||||
|
||||
pub const CreatePairError = error{
|
||||
OperationUnsupported,
|
||||
AccessDenied,
|
||||
AddressFamilyUnsupported,
|
||||
ProtocolUnsupportedBySystem,
|
||||
/// The per-process limit on the number of open file descriptors has been reached.
|
||||
ProcessFdQuotaExceeded,
|
||||
/// The system-wide limit on the total number of open files has been reached.
|
||||
SystemFdQuotaExceeded,
|
||||
/// Insufficient memory is available. The socket cannot be created
|
||||
/// until sufficient resources are freed.
|
||||
SystemResources,
|
||||
ProtocolUnsupportedByAddressFamily,
|
||||
SocketModeUnsupported,
|
||||
} || Io.UnexpectedError || Io.Cancelable;
|
||||
|
||||
pub const CreatePairOptions = struct {
|
||||
family: IpAddress.Family = .ip4,
|
||||
mode: Mode = .stream,
|
||||
protocol: ?Protocol = null,
|
||||
};
|
||||
|
||||
/// Create a set of two sockets that are connected to each other.
|
||||
///
|
||||
/// Also known as "socketpair".
|
||||
pub fn createPair(io: Io, options: CreatePairOptions) CreatePairError![2]Socket {
|
||||
return io.vtable.netSocketCreatePair(io.userdata, options);
|
||||
}
|
||||
};
|
||||
|
||||
/// An open socket connection with a network protocol that guarantees
|
||||
|
||||
+9
-6
@@ -809,12 +809,15 @@ const PosixThreadImpl = struct {
|
||||
else => {
|
||||
var count: c_int = undefined;
|
||||
var count_len: usize = @sizeOf(c_int);
|
||||
const name = if (comptime target.os.tag.isDarwin()) "hw.logicalcpu" else "hw.ncpu";
|
||||
posix.sysctlbynameZ(name, &count, &count_len, null, 0) catch |err| switch (err) {
|
||||
error.UnknownName => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
return @as(usize, @intCast(count));
|
||||
const name = comptime if (target.os.tag.isDarwin()) "hw.logicalcpu" else "hw.ncpu";
|
||||
switch (posix.errno(posix.system.sysctlbyname(name, &count, &count_len, null, 0))) {
|
||||
.SUCCESS => return @intCast(count),
|
||||
.FAULT => unreachable,
|
||||
.PERM => return error.PermissionDenied,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NOENT => unreachable,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1755,7 +1755,7 @@ test "accept multishot" {
|
||||
// connect client
|
||||
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
|
||||
errdefer posix.close(client);
|
||||
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
|
||||
// test accept completion
|
||||
var cqe = try ring.copy_cqe();
|
||||
@@ -1865,7 +1865,7 @@ test "accept_direct" {
|
||||
|
||||
// connect
|
||||
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
|
||||
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
defer posix.close(client);
|
||||
|
||||
// accept completion
|
||||
@@ -1899,7 +1899,7 @@ test "accept_direct" {
|
||||
try testing.expectEqual(@as(u32, 1), try ring.submit());
|
||||
// connect
|
||||
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
|
||||
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
defer posix.close(client);
|
||||
// completion with error
|
||||
const cqe_accept = try ring.copy_cqe();
|
||||
@@ -1949,7 +1949,7 @@ test "accept_multishot_direct" {
|
||||
for (registered_fds) |_| {
|
||||
// connect
|
||||
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
|
||||
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
defer posix.close(client);
|
||||
|
||||
// accept completion
|
||||
@@ -1964,7 +1964,7 @@ test "accept_multishot_direct" {
|
||||
{
|
||||
// connect
|
||||
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
|
||||
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
|
||||
defer posix.close(client);
|
||||
// completion with error
|
||||
const cqe_accept = try ring.copy_cqe();
|
||||
@@ -2734,3 +2734,10 @@ fn send(sockfd: posix.socket_t, buf: []const u8, flags: u32) !usize {
|
||||
else => return error.SendFailed,
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(sock: posix.socket_t, sock_addr: *const posix.sockaddr, len: posix.socklen_t) !void {
|
||||
switch (posix.errno(posix.system.connect(sock, sock_addr, len))) {
|
||||
.SUCCESS => return,
|
||||
else => return error.ConnectFailed,
|
||||
}
|
||||
}
|
||||
|
||||
+7
-347
@@ -1,27 +1,18 @@
|
||||
//! POSIX API layer.
|
||||
//!
|
||||
//! This is more cross platform than using OS-specific APIs, however, it is
|
||||
//! lower-level and less portable than other namespaces such as `std.fs` and
|
||||
//! lower-level and less portable than other namespaces such as `std.Io` and
|
||||
//! `std.process`.
|
||||
//!
|
||||
//! These APIs are generally lowered to libc function calls if and only if libc
|
||||
//! is linked. Most operating systems other than Windows, Linux, and WASI
|
||||
//! require always linking libc because they use it as the stable syscall ABI.
|
||||
//!
|
||||
//! Operating systems that are not POSIX-compliant are sometimes supported by
|
||||
//! this API layer; sometimes not. Generally, an implementation will be
|
||||
//! provided only if such implementation is straightforward on that operating
|
||||
//! system. Otherwise, programmers are expected to use OS-specific logic to
|
||||
//! deal with the exception.
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const native_os = builtin.os.tag;
|
||||
|
||||
const std = @import("std.zig");
|
||||
const Io = std.Io;
|
||||
const mem = std.mem;
|
||||
const fs = std.fs;
|
||||
const max_path_bytes = std.fs.max_path_bytes;
|
||||
const maxInt = std.math.maxInt;
|
||||
const cast = std.math.cast;
|
||||
const assert = std.debug.assert;
|
||||
@@ -122,15 +113,14 @@ pub const STDIN_FILENO = system.STDIN_FILENO;
|
||||
pub const STDOUT_FILENO = system.STDOUT_FILENO;
|
||||
pub const SYS = system.SYS;
|
||||
pub const Sigaction = system.Sigaction;
|
||||
/// Windows has no concept of `stat`.
|
||||
///
|
||||
/// On Linux, the `stat` bits/wrappers are removed due to having to maintain
|
||||
/// the different varying stat structs per target and libc, leading to runtime
|
||||
/// errors. Users targeting Linux should add a comptime check and use statx,
|
||||
/// similar to how `Io.File.stat` does.
|
||||
pub const Stat = switch (native_os) {
|
||||
// Has no concept of `stat`.
|
||||
.windows => void,
|
||||
// The `stat` bits/wrappers are removed due to having to maintain the
|
||||
// different varying `struct stat`s per target and libc, leading to runtime
|
||||
// errors.
|
||||
//
|
||||
// Users targeting linux should add a comptime check and use `statx`,
|
||||
// similar to how `std.fs.File.stat` does.
|
||||
.linux => void,
|
||||
else => system.Stat,
|
||||
};
|
||||
@@ -519,152 +509,6 @@ pub fn getppid() pid_t {
|
||||
return system.getppid();
|
||||
}
|
||||
|
||||
pub const SocketError = error{
|
||||
/// Permission to create a socket of the specified type and/or
|
||||
/// pro‐tocol is denied.
|
||||
AccessDenied,
|
||||
|
||||
/// The implementation does not support the specified address family.
|
||||
AddressFamilyUnsupported,
|
||||
|
||||
/// Unknown protocol, or protocol family not available.
|
||||
ProtocolFamilyNotAvailable,
|
||||
|
||||
/// The per-process limit on the number of open file descriptors has been reached.
|
||||
ProcessFdQuotaExceeded,
|
||||
|
||||
/// The system-wide limit on the total number of open files has been reached.
|
||||
SystemFdQuotaExceeded,
|
||||
|
||||
/// Insufficient memory is available. The socket cannot be created until sufficient
|
||||
/// resources are freed.
|
||||
SystemResources,
|
||||
|
||||
/// The protocol type or the specified protocol is not supported within this domain.
|
||||
ProtocolNotSupported,
|
||||
|
||||
/// The socket type is not supported by the protocol.
|
||||
SocketTypeNotSupported,
|
||||
} || UnexpectedError;
|
||||
|
||||
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.AddressFamilyUnsupported,
|
||||
.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),
|
||||
}
|
||||
}
|
||||
|
||||
fn setSockFlags(sock: socket_t, flags: u32) !void {
|
||||
if ((flags & SOCK.CLOEXEC) != 0) {
|
||||
if (native_os == .windows) {
|
||||
// TODO: Find out if this is supported for sockets
|
||||
} else {
|
||||
var fd_flags = fcntl(sock, F.GETFD, 0) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
error.PermissionDenied => unreachable,
|
||||
error.DeadLock => unreachable,
|
||||
error.LockedRegionLimitExceeded => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
fd_flags |= FD_CLOEXEC;
|
||||
_ = fcntl(sock, F.SETFD, fd_flags) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
error.PermissionDenied => unreachable,
|
||||
error.DeadLock => unreachable,
|
||||
error.LockedRegionLimitExceeded => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
}
|
||||
if ((flags & SOCK.NONBLOCK) != 0) {
|
||||
if (native_os == .windows) {
|
||||
var mode: c_ulong = 1;
|
||||
if (windows.ws2_32.ioctlsocket(sock, windows.ws2_32.FIONBIO, &mode) == windows.ws2_32.SOCKET_ERROR) {
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.NOTINITIALISED => unreachable,
|
||||
.ENETDOWN => return error.NetworkDown,
|
||||
.ENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
// TODO: handle more errors
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var fl_flags = fcntl(sock, F.GETFL, 0) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
error.PermissionDenied => unreachable,
|
||||
error.DeadLock => unreachable,
|
||||
error.LockedRegionLimitExceeded => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
fl_flags |= 1 << @bitOffsetOf(O, "NONBLOCK");
|
||||
_ = fcntl(sock, F.SETFL, fl_flags) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
error.PermissionDenied => unreachable,
|
||||
error.DeadLock => unreachable,
|
||||
error.LockedRegionLimitExceeded => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const EventFdError = error{
|
||||
SystemResources,
|
||||
ProcessFdQuotaExceeded,
|
||||
SystemFdQuotaExceeded,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
|
||||
const rc = system.eventfd(initval, flags);
|
||||
switch (errno(rc)) {
|
||||
.SUCCESS => return @intCast(rc),
|
||||
else => |err| return unexpectedErrno(err),
|
||||
|
||||
.INVAL => unreachable, // invalid parameters
|
||||
.MFILE => return error.ProcessFdQuotaExceeded,
|
||||
.NFILE => return error.SystemFdQuotaExceeded,
|
||||
.NODEV => return error.SystemResources,
|
||||
.NOMEM => return error.SystemResources,
|
||||
}
|
||||
}
|
||||
|
||||
pub const GetSockNameError = error{
|
||||
/// Insufficient resources were available in the system to perform the operation.
|
||||
SystemResources,
|
||||
@@ -707,123 +551,6 @@ pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSock
|
||||
}
|
||||
}
|
||||
|
||||
pub const ConnectError = std.Io.net.IpAddress.ConnectError || std.Io.net.UnixAddress.ConnectError;
|
||||
|
||||
pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
|
||||
if (native_os == .windows) {
|
||||
@compileError("use std.Io instead");
|
||||
}
|
||||
|
||||
while (true) {
|
||||
switch (errno(system.connect(sock, sock_addr, len))) {
|
||||
.SUCCESS => return,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.PERM => return error.PermissionDenied,
|
||||
.ADDRNOTAVAIL => return error.AddressUnavailable,
|
||||
.AFNOSUPPORT => return error.AddressFamilyUnsupported,
|
||||
.AGAIN, .INPROGRESS => return error.WouldBlock,
|
||||
.ALREADY => return error.ConnectionPending,
|
||||
.BADF => unreachable, // sockfd is not a valid open file descriptor.
|
||||
.CONNREFUSED => return error.ConnectionRefused,
|
||||
.CONNRESET => return error.ConnectionResetByPeer,
|
||||
.FAULT => unreachable, // The socket structure address is outside the user's address space.
|
||||
.INTR => continue,
|
||||
.ISCONN => @panic("AlreadyConnected"), // The socket is already connected.
|
||||
.HOSTUNREACH => return error.NetworkUnreachable,
|
||||
.NETUNREACH => return error.NetworkUnreachable,
|
||||
.NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
|
||||
.PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
|
||||
.TIMEDOUT => return error.Timeout,
|
||||
.NOENT => return error.FileNotFound, // Returned when socket is AF.UNIX and the given path does not exist.
|
||||
.CONNABORTED => unreachable, // Tried to reuse socket that previously received error.ConnectionRefused.
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const FStatError = std.Io.File.StatError;
|
||||
|
||||
/// Return information about a file descriptor.
|
||||
pub fn fstat(fd: fd_t) FStatError!Stat {
|
||||
if (native_os == .wasi and !builtin.link_libc) {
|
||||
@compileError("unsupported OS");
|
||||
}
|
||||
|
||||
var stat = mem.zeroes(Stat);
|
||||
switch (errno(system.fstat(fd, &stat))) {
|
||||
.SUCCESS => return stat,
|
||||
.INVAL => unreachable,
|
||||
.BADF => unreachable, // Always a race condition.
|
||||
.NOMEM => return error.SystemResources,
|
||||
.ACCES => return error.AccessDenied,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub const INotifyInitError = error{
|
||||
ProcessFdQuotaExceeded,
|
||||
SystemFdQuotaExceeded,
|
||||
SystemResources,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// initialize an inotify instance
|
||||
pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
|
||||
const rc = system.inotify_init1(flags);
|
||||
switch (errno(rc)) {
|
||||
.SUCCESS => return @intCast(rc),
|
||||
.INVAL => unreachable,
|
||||
.MFILE => return error.ProcessFdQuotaExceeded,
|
||||
.NFILE => return error.SystemFdQuotaExceeded,
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub const INotifyAddWatchError = error{
|
||||
AccessDenied,
|
||||
NameTooLong,
|
||||
FileNotFound,
|
||||
SystemResources,
|
||||
UserResourceLimitReached,
|
||||
NotDir,
|
||||
WatchAlreadyExists,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// add a watch to an initialized inotify instance
|
||||
pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
|
||||
const pathname_c = try toPosixPath(pathname);
|
||||
return inotify_add_watchZ(inotify_fd, &pathname_c, mask);
|
||||
}
|
||||
|
||||
/// Same as `inotify_add_watch` except pathname is null-terminated.
|
||||
pub fn inotify_add_watchZ(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) INotifyAddWatchError!i32 {
|
||||
const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
|
||||
switch (errno(rc)) {
|
||||
.SUCCESS => return @intCast(rc),
|
||||
.ACCES => return error.AccessDenied,
|
||||
.BADF => unreachable,
|
||||
.FAULT => unreachable,
|
||||
.INVAL => unreachable,
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NOSPC => return error.UserResourceLimitReached,
|
||||
.NOTDIR => return error.NotDir,
|
||||
.EXIST => return error.WatchAlreadyExists,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// remove an existing watch from an inotify instance
|
||||
pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
|
||||
switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
|
||||
.SUCCESS => return,
|
||||
.BADF => unreachable,
|
||||
.INVAL => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub const FanotifyInitError = error{
|
||||
ProcessFdQuotaExceeded,
|
||||
SystemFdQuotaExceeded,
|
||||
@@ -1060,73 +787,6 @@ pub fn sysctl(
|
||||
}
|
||||
}
|
||||
|
||||
pub const SysCtlByNameError = error{
|
||||
PermissionDenied,
|
||||
SystemResources,
|
||||
UnknownName,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn sysctlbynameZ(
|
||||
name: [*:0]const u8,
|
||||
oldp: ?*anyopaque,
|
||||
oldlenp: ?*usize,
|
||||
newp: ?*anyopaque,
|
||||
newlen: usize,
|
||||
) SysCtlByNameError!void {
|
||||
if (native_os == .wasi) {
|
||||
@compileError("sysctl not supported on WASI");
|
||||
}
|
||||
if (native_os == .haiku) {
|
||||
@compileError("sysctl not supported on Haiku");
|
||||
}
|
||||
|
||||
switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
|
||||
.SUCCESS => return,
|
||||
.FAULT => unreachable,
|
||||
.PERM => return error.PermissionDenied,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NOENT => return error.UnknownName,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
|
||||
switch (errno(system.gettimeofday(tv, tz))) {
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub const FcntlError = error{
|
||||
PermissionDenied,
|
||||
FileBusy,
|
||||
ProcessFdQuotaExceeded,
|
||||
Locked,
|
||||
DeadLock,
|
||||
LockedRegionLimitExceeded,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
|
||||
while (true) {
|
||||
const rc = system.fcntl(fd, cmd, arg);
|
||||
switch (errno(rc)) {
|
||||
.SUCCESS => return @intCast(rc),
|
||||
.INTR => continue,
|
||||
.AGAIN, .ACCES => return error.Locked,
|
||||
.BADF => unreachable,
|
||||
.BUSY => return error.FileBusy,
|
||||
.INVAL => unreachable, // invalid parameters
|
||||
.PERM => return error.PermissionDenied,
|
||||
.MFILE => return error.ProcessFdQuotaExceeded,
|
||||
.NOTDIR => unreachable, // invalid parameter
|
||||
.DEADLK => return error.DeadLock,
|
||||
.NOLCK => return error.LockedRegionLimitExceeded,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getSelfPhdrs() []std.elf.ElfN.Phdr {
|
||||
const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval;
|
||||
assert(getauxval(std.elf.AT_PHENT) == @sizeOf(std.elf.ElfN.Phdr));
|
||||
|
||||
@@ -273,17 +273,17 @@ test "fcntl" {
|
||||
|
||||
// Note: The test assumes createFile opens the file with CLOEXEC
|
||||
{
|
||||
const flags = try posix.fcntl(file.handle, posix.F.GETFD, 0);
|
||||
const flags = posix.system.fcntl(file.handle, posix.F.GETFD, @as(usize, 0));
|
||||
try expect((flags & posix.FD_CLOEXEC) != 0);
|
||||
}
|
||||
{
|
||||
_ = try posix.fcntl(file.handle, posix.F.SETFD, 0);
|
||||
const flags = try posix.fcntl(file.handle, posix.F.GETFD, 0);
|
||||
_ = posix.system.fcntl(file.handle, posix.F.SETFD, @as(usize, 0));
|
||||
const flags = posix.system.fcntl(file.handle, posix.F.GETFD, @as(usize, 0));
|
||||
try expect((flags & posix.FD_CLOEXEC) == 0);
|
||||
}
|
||||
{
|
||||
_ = try posix.fcntl(file.handle, posix.F.SETFD, posix.FD_CLOEXEC);
|
||||
const flags = try posix.fcntl(file.handle, posix.F.GETFD, 0);
|
||||
_ = posix.system.fcntl(file.handle, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC));
|
||||
const flags = posix.system.fcntl(file.handle, posix.F.GETFD, @as(usize, 0));
|
||||
try expect((flags & posix.FD_CLOEXEC) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-12
@@ -556,26 +556,28 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
|
||||
const name = if (native_os == .netbsd) "hw.physmem64" else "hw.physmem";
|
||||
var physmem: c_ulong = undefined;
|
||||
var len: usize = @sizeOf(c_ulong);
|
||||
posix.sysctlbynameZ(name, &physmem, &len, null, 0) catch |err| switch (err) {
|
||||
error.PermissionDenied => unreachable, // only when setting values,
|
||||
error.SystemResources => unreachable, // memory already on the stack
|
||||
error.UnknownName => unreachable,
|
||||
switch (posix.errno(posix.system.sysctlbyname(name, &physmem, &len, null, 0))) {
|
||||
.SUCCESS => return @intCast(physmem),
|
||||
.FAULT => unreachable,
|
||||
.PERM => unreachable, // only when setting values
|
||||
.NOMEM => unreachable, // memory already on the stack
|
||||
.NOENT => unreachable,
|
||||
else => return error.UnknownTotalSystemMemory,
|
||||
};
|
||||
return @intCast(physmem);
|
||||
}
|
||||
},
|
||||
// whole Darwin family
|
||||
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
|
||||
// "hw.memsize" returns uint64_t
|
||||
var physmem: u64 = undefined;
|
||||
var len: usize = @sizeOf(u64);
|
||||
posix.sysctlbynameZ("hw.memsize", &physmem, &len, null, 0) catch |err| switch (err) {
|
||||
error.PermissionDenied => unreachable, // only when setting values,
|
||||
error.SystemResources => unreachable, // memory already on the stack
|
||||
error.UnknownName => unreachable, // constant, known good value
|
||||
switch (posix.errno(posix.system.sysctlbyname("hw.memsize", &physmem, &len, null, 0))) {
|
||||
.SUCCESS => return physmem,
|
||||
.FAULT => unreachable,
|
||||
.PERM => unreachable, // only when setting values
|
||||
.NOMEM => unreachable, // memory already on the stack
|
||||
.NOENT => unreachable, // constant, known good value
|
||||
else => return error.UnknownTotalSystemMemory,
|
||||
};
|
||||
return physmem;
|
||||
}
|
||||
},
|
||||
.openbsd => {
|
||||
const mib: [2]c_int = [_]c_int{
|
||||
|
||||
@@ -260,12 +260,14 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
|
||||
var value: u32 = undefined;
|
||||
var len: usize = @sizeOf(@TypeOf(value));
|
||||
|
||||
posix.sysctlbynameZ(key, &value, &len, null, 0) catch |err| switch (err) {
|
||||
error.PermissionDenied => unreachable, // only when setting values,
|
||||
error.SystemResources => unreachable, // memory already on the stack
|
||||
error.UnknownName => unreachable, // constant, known good value
|
||||
error.Unexpected => return error.OSVersionDetectionFail,
|
||||
};
|
||||
switch (posix.errno(posix.system.sysctlbyname(key, &value, &len, null, 0))) {
|
||||
.SUCCESS => {},
|
||||
.FAULT => unreachable,
|
||||
.PERM => unreachable, // only when setting values,
|
||||
.NOMEM => unreachable, // memory already on the stack
|
||||
.NOENT => unreachable, // constant, known good value
|
||||
else => return error.OSVersionDetectionFail,
|
||||
}
|
||||
|
||||
switch (builtin.target.os.tag) {
|
||||
.freebsd => {
|
||||
|
||||
@@ -2,6 +2,7 @@ const builtin = @import("builtin");
|
||||
|
||||
const std = @import("std");
|
||||
const Io = std.Io;
|
||||
const posix = std.posix;
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
@@ -399,12 +400,14 @@ test "detect" {
|
||||
pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
|
||||
var cpu_family: std.c.CPUFAMILY = undefined;
|
||||
var len: usize = @sizeOf(std.c.CPUFAMILY);
|
||||
std.posix.sysctlbynameZ("hw.cpufamily", &cpu_family, &len, null, 0) catch |err| switch (err) {
|
||||
error.PermissionDenied => unreachable, // only when setting values,
|
||||
error.SystemResources => unreachable, // memory already on the stack
|
||||
error.UnknownName => unreachable, // constant, known good value
|
||||
error.Unexpected => unreachable, // EFAULT: stack should be safe, EISDIR/ENOTDIR: constant, known good value
|
||||
};
|
||||
switch (posix.errno(posix.system.sysctlbyname("hw.cpufamily", &cpu_family, &len, null, 0))) {
|
||||
.SUCCESS => {},
|
||||
.FAULT => unreachable, // segmentation fault
|
||||
.PERM => unreachable, // only when setting values,
|
||||
.NOMEM => unreachable, // memory already on the stack
|
||||
.NOENT => unreachable, // constant, known good value
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const current_arch = builtin.cpu.arch;
|
||||
switch (current_arch) {
|
||||
|
||||
Reference in New Issue
Block a user