diff options
author | 2022-01-23 23:00:15 -0800 | |
---|---|---|
committer | 2022-01-23 23:00:15 -0800 | |
commit | de0cf421115ed3814ccc4d46e4d6202aac792ba0 (patch) | |
tree | 8c3f609e9497c30ae2f7512a5b5d7b8e0d37595f | |
parent | ec9e4eb97e0633e508606af9d18e76a3c63647da (diff) | |
download | bun-de0cf421115ed3814ccc4d46e4d6202aac792ba0.tar.gz bun-de0cf421115ed3814ccc4d46e4d6202aac792ba0.tar.zst bun-de0cf421115ed3814ccc4d46e4d6202aac792ba0.zip |
Use non-cancellable syscalls for HTTP & use errno for errors
-rw-r--r-- | src/http_client_async.zig | 6 | ||||
-rw-r--r-- | src/io/io_darwin.zig | 532 | ||||
-rw-r--r-- | src/io/io_linux.zig | 473 |
3 files changed, 933 insertions, 78 deletions
diff --git a/src/http_client_async.zig b/src/http_client_async.zig index d0ab75fab..21bc183be 100644 --- a/src/http_client_async.zig +++ b/src/http_client_async.zig @@ -588,7 +588,11 @@ const AsyncSocket = struct { connect_completion: AsyncIO.Completion = undefined, close_completion: AsyncIO.Completion = undefined, - const ConnectError = AsyncIO.ConnectError || std.os.SocketError || std.os.SetSockOptError || error{UnknownHostName}; + const ConnectError = AsyncIO.ConnectError || std.os.SocketError || std.os.SetSockOptError || error{ + UnknownHostName, + ConnectionRefused, + AddressNotAvailable, + }; pub fn init(io: *AsyncIO, socket: std.os.socket_t, allocator: std.mem.Allocator) !AsyncSocket { var head = AsyncMessage.get(allocator); diff --git a/src/io/io_darwin.zig b/src/io/io_darwin.zig index c072cd4bd..97ccd6d23 100644 --- a/src/io/io_darwin.zig +++ b/src/io/io_darwin.zig @@ -15,9 +15,383 @@ const os = struct { pub const EOVERFLOW = 84; pub const ESPIPE = 29; }; + +const SystemErrno = @import("../darwin_c.zig").SystemErrno; +pub const Errno = error{ + EPERM, + ENOENT, + ESRCH, + EINTR, + EIO, + ENXIO, + E2BIG, + ENOEXEC, + EBADF, + ECHILD, + EDEADLK, + ENOMEM, + EACCES, + EFAULT, + ENOTBLK, + EBUSY, + EEXIST, + EXDEV, + ENODEV, + ENOTDIR, + EISDIR, + EINVAL, + ENFILE, + EMFILE, + ENOTTY, + ETXTBSY, + EFBIG, + ENOSPC, + ESPIPE, + EROFS, + EMLINK, + EPIPE, + EDOM, + ERANGE, + EAGAIN, + EINPROGRESS, + EALREADY, + ENOTSOCK, + EDESTADDRREQ, + EMSGSIZE, + EPROTOTYPE, + ENOPROTOOPT, + EPROTONOSUPPORT, + ESOCKTNOSUPPORT, + ENOTSUP, + EPFNOSUPPORT, + EAFNOSUPPORT, + EADDRINUSE, + EADDRNOTAVAIL, + ENETDOWN, + ENETUNREACH, + ENETRESET, + ECONNABORTED, + ECONNRESET, + ENOBUFS, + EISCONN, + ENOTCONN, + ESHUTDOWN, + ETOOMANYREFS, + ETIMEDOUT, + ECONNREFUSED, + ELOOP, + ENAMETOOLONG, + EHOSTDOWN, + EHOSTUNREACH, + ENOTEMPTY, + EPROCLIM, + EUSERS, + EDQUOT, + ESTALE, + EREMOTE, + EBADRPC, + ERPCMISMATCH, + EPROGUNAVAIL, + EPROGMISMATCH, + EPROCUNAVAIL, + ENOLCK, + ENOSYS, + EFTYPE, + EAUTH, + ENEEDAUTH, + EPWROFF, + EDEVERR, + EOVERFLOW, + EBADEXEC, + EBADARCH, + ESHLIBVERS, + EBADMACHO, + ECANCELED, + EIDRM, + ENOMSG, + EILSEQ, + ENOATTR, + EBADMSG, + EMULTIHOP, + ENODATA, + ENOLINK, + ENOSR, + ENOSTR, + EPROTO, + ETIME, + EOPNOTSUPP, + ENOPOLICY, + ENOTRECOVERABLE, + EOWNERDEAD, + EQFULL, + Unexpected, +}; + +const errno_map: [108]Errno = brk: { + var errors: [108]Errno = undefined; + errors[1] = error.EPERM; + errors[2] = error.ENOENT; + errors[3] = error.ESRCH; + errors[4] = error.EINTR; + errors[5] = error.EIO; + errors[6] = error.ENXIO; + errors[7] = error.E2BIG; + errors[8] = error.ENOEXEC; + errors[9] = error.EBADF; + errors[10] = error.ECHILD; + errors[11] = error.EDEADLK; + errors[12] = error.ENOMEM; + errors[13] = error.EACCES; + errors[14] = error.EFAULT; + errors[15] = error.ENOTBLK; + errors[16] = error.EBUSY; + errors[17] = error.EEXIST; + errors[18] = error.EXDEV; + errors[19] = error.ENODEV; + errors[20] = error.ENOTDIR; + errors[21] = error.EISDIR; + errors[22] = error.EINVAL; + errors[23] = error.ENFILE; + errors[24] = error.EMFILE; + errors[25] = error.ENOTTY; + errors[26] = error.ETXTBSY; + errors[27] = error.EFBIG; + errors[28] = error.ENOSPC; + errors[29] = error.ESPIPE; + errors[30] = error.EROFS; + errors[31] = error.EMLINK; + errors[32] = error.EPIPE; + errors[33] = error.EDOM; + errors[34] = error.ERANGE; + errors[35] = error.EAGAIN; + errors[36] = error.EINPROGRESS; + errors[37] = error.EALREADY; + errors[38] = error.ENOTSOCK; + errors[39] = error.EDESTADDRREQ; + errors[40] = error.EMSGSIZE; + errors[41] = error.EPROTOTYPE; + errors[42] = error.ENOPROTOOPT; + errors[43] = error.EPROTONOSUPPORT; + errors[44] = error.ESOCKTNOSUPPORT; + errors[45] = error.ENOTSUP; + errors[46] = error.EPFNOSUPPORT; + errors[47] = error.EAFNOSUPPORT; + errors[48] = error.EADDRINUSE; + errors[49] = error.EADDRNOTAVAIL; + errors[50] = error.ENETDOWN; + errors[51] = error.ENETUNREACH; + errors[52] = error.ENETRESET; + errors[53] = error.ECONNABORTED; + errors[54] = error.ECONNRESET; + errors[55] = error.ENOBUFS; + errors[56] = error.EISCONN; + errors[57] = error.ENOTCONN; + errors[58] = error.ESHUTDOWN; + errors[59] = error.ETOOMANYREFS; + errors[60] = error.ETIMEDOUT; + errors[61] = error.ECONNREFUSED; + errors[62] = error.ELOOP; + errors[63] = error.ENAMETOOLONG; + errors[64] = error.EHOSTDOWN; + errors[65] = error.EHOSTUNREACH; + errors[66] = error.ENOTEMPTY; + errors[67] = error.EPROCLIM; + errors[68] = error.EUSERS; + errors[69] = error.EDQUOT; + errors[70] = error.ESTALE; + errors[71] = error.EREMOTE; + errors[72] = error.EBADRPC; + errors[73] = error.ERPCMISMATCH; + errors[74] = error.EPROGUNAVAIL; + errors[75] = error.EPROGMISMATCH; + errors[76] = error.EPROCUNAVAIL; + errors[77] = error.ENOLCK; + errors[78] = error.ENOSYS; + errors[79] = error.EFTYPE; + errors[80] = error.EAUTH; + errors[81] = error.ENEEDAUTH; + errors[82] = error.EPWROFF; + errors[83] = error.EDEVERR; + errors[84] = error.EOVERFLOW; + errors[85] = error.EBADEXEC; + errors[86] = error.EBADARCH; + errors[87] = error.ESHLIBVERS; + errors[88] = error.EBADMACHO; + errors[89] = error.ECANCELED; + errors[90] = error.EIDRM; + errors[91] = error.ENOMSG; + errors[92] = error.EILSEQ; + errors[93] = error.ENOATTR; + errors[94] = error.EBADMSG; + errors[95] = error.EMULTIHOP; + errors[96] = error.ENODATA; + errors[97] = error.ENOLINK; + errors[98] = error.ENOSR; + errors[99] = error.ENOSTR; + errors[100] = error.EPROTO; + errors[101] = error.ETIME; + errors[102] = error.EOPNOTSUPP; + errors[103] = error.ENOPOLICY; + errors[104] = error.ENOTRECOVERABLE; + errors[105] = error.EOWNERDEAD; + errors[106] = error.EQFULL; + break :brk errors; +}; + +const socket_t = os.socket_t; +const sockaddr = darwin.sockaddr; +const socklen_t = darwin.socklen_t; +const system = darwin; + +pub fn asError(err: anytype) Errno { + return switch (@enumToInt(err)) { + 1...errno_map.len => |val| errno_map[@intCast(u8, val)], + else => error.Unexpected, + }; +} +const fd_t = os.fd_t; + const mem = std.mem; const assert = std.debug.assert; const c = std.c; +const darwin = struct { + pub usingnamespace os.darwin; + pub extern "c" fn @"recvfrom$NOCANCEL"(sockfd: c.fd_t, noalias buf: *anyopaque, len: usize, flags: u32, noalias src_addr: ?*c.sockaddr, noalias addrlen: ?*c.socklen_t) isize; + pub extern "c" fn @"sendto$NOCANCEL"( + sockfd: c.fd_t, + buf: *const anyopaque, + len: usize, + flags: u32, + dest_addr: ?*const c.sockaddr, + addrlen: c.socklen_t, + ) isize; + pub extern "c" fn @"fcntl$NOCANCEL"(fd: c.fd_t, cmd: c_int, ...) c_int; + pub extern "c" fn @"sendmsg$NOCANCEL"(sockfd: c.fd_t, msg: *const std.x.os.Socket.Message, flags: c_int) isize; + pub extern "c" fn @"recvmsg$NOCANCEL"(sockfd: c.fd_t, msg: *std.x.os.Socket.Message, flags: c_int) isize; + pub extern "c" fn @"connect$NOCANCEL"(sockfd: c.fd_t, sock_addr: *const c.sockaddr, addrlen: c.socklen_t) c_int; + pub extern "c" fn @"accept$NOCANCEL"(sockfd: c.fd_t, noalias addr: ?*c.sockaddr, noalias addrlen: ?*c.socklen_t) c_int; + pub extern "c" fn @"accept4$NOCANCEL"(sockfd: c.fd_t, noalias addr: ?*c.sockaddr, noalias addrlen: ?*c.socklen_t, flags: c_uint) c_int; +}; + +pub const Syscall = struct { + pub fn close(fd: std.os.fd_t) CloseError!void { + return switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) { + .SUCCESS => void{}, + .BADF => error.FileDescriptorInvalid, + .IO => error.InputOutput, + else => |err| asError(err), + }; + } + + pub const SocketError = error{ + /// Permission to create a socket of the specified type and/or + /// pro‐tocol is denied. + PermissionDenied, + + /// The implementation does not support the specified address family. + AddressFamilyNotSupported, + + /// 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, + } || Errno; + const SOCK = os.SOCK; + const FD_CLOEXEC = os.FD_CLOEXEC; + pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) Errno!usize { + const rc = darwin.@"fcntl$NOCANCEL"(fd, cmd, arg); + return switch (darwin.getErrno(rc)) { + .SUCCESS => @intCast(usize, rc), + else => |err| asError(err), + }; + } + + const F = std.os.F; + const O = std.os.O; + pub fn setSockFlags(sock: socket_t, flags: u32) !void { + if ((flags & SOCK.CLOEXEC) != 0) { + var fd_flags = try fcntl(sock, F.GETFD, 0); + fd_flags |= FD_CLOEXEC; + _ = try fcntl(sock, F.SETFD, fd_flags); + } + + if ((flags & SOCK.NONBLOCK) != 0) { + var fl_flags = try fcntl(sock, F.GETFL, 0); + fl_flags |= O.NONBLOCK; + _ = try fcntl(sock, F.SETFL, fl_flags); + } + } + + pub const SetSockOptError = error{ + /// The socket is already connected, and a specified option cannot be set while the socket is connected. + AlreadyConnected, + + /// The option is not supported by the protocol. + InvalidProtocolOption, + + /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure. + TimeoutTooBig, + + /// Insufficient resources are available in the system to complete the call. + SystemResources, + + // Setting the socket option requires more elevated permissions. + PermissionDenied, + + NetworkSubsystemFailed, + FileDescriptorNotASocket, + SocketNotBound, + } || Errno; + + pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void { + switch (darwin.getErrno(darwin.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) { + .SUCCESS => {}, + .DOM => return error.TimeoutTooBig, + .ISCONN => return error.AlreadyConnected, + .NOPROTOOPT => return error.InvalidProtocolOption, + .NOMEM => return error.SystemResources, + .NOBUFS => return error.SystemResources, + .PERM => return error.PermissionDenied, + else => |err| return asError(err), + } + } + + pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t { + const filtered_sock_type = socket_type & ~@as(u32, os.SOCK.NONBLOCK | os.SOCK.CLOEXEC); + const rc = darwin.socket(domain, filtered_sock_type, protocol); + switch (darwin.getErrno(rc)) { + .SUCCESS => { + const fd = @intCast(fd_t, rc); + try setSockFlags(fd, socket_type); + return fd; + }, + .ACCES => return error.PermissionDenied, + .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 asError(err), + } + } +}; const FIFO = @import("./fifo.zig").FIFO; const Time = @import("./time.zig").Time; @@ -217,7 +591,7 @@ fn flush_timeouts(self: *IO) ?u64 { const timeout_ns = expires - now; if (min_timeout) |min_ns| { - min_timeout = std.math.min(min_ns, timeout_ns); + min_timeout = @minimum(min_ns, timeout_ns); } else { min_timeout = timeout_ns; } @@ -296,7 +670,7 @@ fn submit( const result = OperationImpl.doOperation(op_data); // Requeue onto io_pending if error.WouldBlock - switch (operation_tag) { + switch (comptime operation_tag) { .accept, .connect, .read, .write, .send, .recv => { _ = result catch |err| switch (err) { error.WouldBlock => { @@ -423,11 +797,13 @@ pub fn accept( null, os.SOCK.NONBLOCK | os.SOCK.CLOEXEC, ); - errdefer os.close(fd); + errdefer { + Syscall.close(fd) catch {}; + } // darwin doesn't support os.MSG.NOSIGNAL, // but instead a socket option to avoid SIGPIPE. - os.setsockopt(fd, os.SOL_SOCKET, os.SO_NOSIGPIPE, &mem.toBytes(@as(c_int, 1))) catch |err| return switch (err) { + Syscall.setsockopt(fd, os.SOL_SOCKET, os.SO_NOSIGPIPE, &mem.toBytes(@as(c_int, 1))) catch |err| return switch (err) { error.TimeoutTooBig => unreachable, error.PermissionDenied => error.NetworkSubsystemFailed, error.AlreadyConnected => error.NetworkSubsystemFailed, @@ -446,7 +822,7 @@ pub const CloseError = error{ DiskQuota, InputOutput, NoSpaceLeft, -} || os.UnexpectedError; +} || Errno; pub fn close( self: *IO, @@ -470,19 +846,25 @@ pub fn close( }, struct { fn doOperation(op: anytype) CloseError!void { - return switch (os.system.close(op.fd)) { - 0 => {}, - os.EBADF => error.FileDescriptorInvalid, - os.EINTR => {}, // A success, see https://github.com/ziglang/zig/issues/2425 - os.EIO => error.InputOutput, - else => |errno| os.unexpectedErrno(os.errno(errno)), - }; + return Syscall.close(op.fd); } }, ); } -pub const ConnectError = os.ConnectError; +pub const ConnectError = error{ + AddressFamilyNotSupported, + AddressInUse, + AddressNotAvailable, + ConnectionPending, + ConnectionRefused, + ConnectionResetByPeer, + ConnectionTimedOut, + NetworkUnreachable, + PermissionDenied, + SystemResources, + WouldBlock, +} || Errno; pub fn connect( self: *IO, @@ -491,7 +873,7 @@ pub fn connect( comptime callback: fn ( context: Context, completion: *Completion, - result: ConnectError!void, + result: IO.ConnectError!void, ) void, completion: *Completion, socket: os.socket_t, @@ -508,12 +890,53 @@ pub fn connect( .initiated = false, }, struct { - fn doOperation(op: anytype) ConnectError!void { + fn doOperation(op: anytype) IO.ConnectError!void { // Don't call connect after being rescheduled by io_pending as it gives EISCONN. // Instead, check the socket error to see if has been connected successfully. const result = switch (op.initiated) { - true => os.getsockoptError(op.socket), - else => os.connect(op.socket, &op.address.any, op.address.getOsSockLen()), + true => brk: { + var err_code: i32 = undefined; + var size: u32 = @sizeOf(u32); + const rc = system.getsockopt(op.socket, os.SOL.SOCKET, os.SO.ERROR, @ptrCast([*]u8, &err_code), &size); + assert(size == 4); + break :brk switch (darwin.getErrno(rc)) { + .SUCCESS => switch (@intToEnum(os.E, err_code)) { + .SUCCESS => void{}, + .ACCES => error.PermissionDenied, + .PERM => error.PermissionDenied, + .ADDRINUSE => error.AddressInUse, + .ADDRNOTAVAIL => error.AddressNotAvailable, + .AFNOSUPPORT => error.AddressFamilyNotSupported, + .AGAIN => error.SystemResources, + .ALREADY => error.ConnectionPending, + .CONNREFUSED => error.ConnectionRefused, + // .FAULT => unreachable, // The socket structure address is outside the user's address space. + // .ISCONN => unreachable, // The socket is already connected. + .NETUNREACH => 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 => error.ConnectionTimedOut, + .CONNRESET => error.ConnectionResetByPeer, + else => |err| asError(err), + }, + else => |err| asError(err), + }; + }, + else => switch (darwin.getErrno(darwin.@"connect$NOCANCEL"(op.socket, &op.address.any, op.address.getOsSockLen()))) { + .SUCCESS => void{}, + .ACCES => error.PermissionDenied, + .PERM => error.PermissionDenied, + .ADDRINUSE => error.AddressInUse, + .ADDRNOTAVAIL => error.AddressNotAvailable, + .AFNOSUPPORT => error.AddressFamilyNotSupported, + .AGAIN, .INPROGRESS => error.WouldBlock, + .ALREADY => error.ConnectionPending, + .CONNREFUSED => error.ConnectionRefused, + .CONNRESET => error.ConnectionResetByPeer, + .NETUNREACH => error.NetworkUnreachable, + .TIMEDOUT => error.ConnectionTimedOut, + else => |err| asError(err), + }, }; op.initiated = true; @@ -562,7 +985,7 @@ pub const ReadError = error{ IsDir, SystemResources, Unseekable, -} || os.UnexpectedError; +} || Errno; pub fn read( self: *IO, @@ -604,7 +1027,6 @@ pub fn read( os.EAGAIN => error.WouldBlock, os.EBADF => error.NotOpenForReading, os.ECONNRESET => error.ConnectionResetByPeer, - os.EFAULT => unreachable, os.EINVAL => error.Alignment, os.EIO => error.InputOutput, os.EISDIR => error.IsDir, @@ -613,7 +1035,7 @@ pub fn read( os.ENXIO => error.Unseekable, os.EOVERFLOW => error.Unseekable, os.ESPIPE => error.Unseekable, - else => error.Unexpected, + else => |err| asError(err), }; } } @@ -621,7 +1043,12 @@ pub fn read( ); } -pub const RecvError = os.RecvFromError; +pub const RecvError = error{ + SystemResources, + ConnectionRefused, + ConnectionResetByPeer, + WouldBlock, +} || Errno; pub fn recv( self: *IO, @@ -648,13 +1075,37 @@ pub fn recv( }, struct { fn doOperation(op: anytype) RecvError!usize { - return os.recv(op.socket, op.buf[0..op.len], 0); + const rc = system.@"recvfrom$NOCANCEL"(op.socket, op.buf, op.len, 0, null, null); + return switch (system.getErrno(rc)) { + .SUCCESS => @intCast(usize, rc), + .AGAIN => error.WouldBlock, + .NOMEM => error.SystemResources, + .CONNREFUSED => error.ConnectionRefused, + .CONNRESET => error.ConnectionResetByPeer, + else => |err| asError(err), + }; } }, ); } -pub const SendError = os.SendToError; +pub const SendError = error{ + AccessDenied, + AddressFamilyNotSupported, + BrokenPipe, + ConnectionResetByPeer, + FastOpenAlreadyInProgress, + FileNotFound, + MessageTooBig, + NameTooLong, + NetworkSubsystemFailed, + NetworkUnreachable, + NotDir, + SocketNotConnected, + SymLinkLoop, + SystemResources, + WouldBlock, +} || Errno; pub fn send( self: *IO, @@ -683,13 +1134,34 @@ pub fn send( }, struct { fn doOperation(op: anytype) SendError!usize { - return os.sendto(op.socket, op.buf[0..op.len], op.flags, null, 0); + const rc = system.@"sendto$NOCANCEL"(op.socket, op.buf, op.len, op.flags, null, 0); + return switch (system.getErrno(rc)) { + .SUCCESS => @intCast(usize, rc), + .ACCES => error.AccessDenied, + .AGAIN => error.WouldBlock, + .ALREADY => error.FastOpenAlreadyInProgress, + .CONNRESET => error.ConnectionResetByPeer, + .MSGSIZE => error.MessageTooBig, + .NOBUFS => error.SystemResources, + .NOMEM => error.SystemResources, + .PIPE => error.BrokenPipe, + .AFNOSUPPORT => error.AddressFamilyNotSupported, + .LOOP => error.SymLinkLoop, + .NAMETOOLONG => error.NameTooLong, + .NOENT => error.FileNotFound, + .NOTDIR => error.NotDir, + .HOSTUNREACH => error.NetworkUnreachable, + .NETUNREACH => error.NetworkUnreachable, + .NOTCONN => error.SocketNotConnected, + .NETDOWN => error.NetworkSubsystemFailed, + else => |err| asError(err), + }; } }, ); } -pub const TimeoutError = error{Canceled} || os.UnexpectedError; +pub const TimeoutError = error{Canceled} || Errno; pub fn timeout( self: *IO, @@ -755,11 +1227,13 @@ pub fn write( } pub fn openSocket(family: u32, sock_type: u32, protocol: u32) !os.socket_t { - const fd = try os.socket(family, sock_type | os.SOCK.NONBLOCK, protocol); - errdefer os.close(fd); + const fd = try Syscall.socket(family, sock_type | os.SOCK.NONBLOCK, protocol); + errdefer { + Syscall.close(fd) catch {}; + } // darwin doesn't support os.MSG.NOSIGNAL, but instead a socket option to avoid SIGPIPE. - try os.setsockopt(fd, os.SOL.SOCKET, os.SO.NOSIGPIPE, &mem.toBytes(@as(c_int, 1))); + try Syscall.setsockopt(fd, os.SOL.SOCKET, os.SO.NOSIGPIPE, &mem.toBytes(@as(c_int, 1))); return fd; } @@ -775,7 +1249,7 @@ fn buffer_limit(buffer_len: usize) usize { .macos, .ios, .watchos, .tvos => std.math.maxInt(i32), else => std.math.maxInt(isize), }; - return std.math.min(limit, buffer_len); + return @minimum(limit, buffer_len); } pub var global: IO = undefined; diff --git a/src/io/io_linux.zig b/src/io/io_linux.zig index 7abb01cc3..ea6c29284 100644 --- a/src/io/io_linux.zig +++ b/src/io/io_linux.zig @@ -2,49 +2,426 @@ const std = @import("std"); const assert = std.debug.assert; const os = struct { pub usingnamespace std.os; - pub const ETIME = 62; + pub const EPERM = 1; + pub const ENOENT = 2; + pub const ESRCH = 3; pub const EINTR = 4; - pub const EAGAIN = 11; + pub const EIO = 5; + pub const ENXIO = 6; + pub const E2BIG = 7; + pub const ENOEXEC = 8; pub const EBADF = 9; - pub const ECONNABORTED = 103; + pub const ECHILD = 10; + pub const EAGAIN = 11; + pub const ENOMEM = 12; + pub const EACCES = 13; pub const EFAULT = 14; + pub const ENOTBLK = 15; + pub const EBUSY = 16; + pub const EEXIST = 17; + pub const EXDEV = 18; + pub const ENODEV = 19; + pub const ENOTDIR = 20; + pub const EISDIR = 21; pub const EINVAL = 22; - pub const EMFILE = 24; pub const ENFILE = 23; - pub const ENOBUFS = 105; - pub const ENOMEM = 12; - pub const ENOTSOCK = 88; - pub const EOPNOTSUPP = 95; - pub const EPERM = 1; - pub const EPROTO = 71; - pub const EDQUOT = 69; - pub const EIO = 5; + pub const EMFILE = 24; + pub const ENOTTY = 25; + pub const ETXTBSY = 26; + pub const EFBIG = 27; pub const ENOSPC = 28; - pub const EACCES = 13; + pub const ESPIPE = 29; + pub const EROFS = 30; + pub const EMLINK = 31; + pub const EPIPE = 32; + pub const EDOM = 33; + pub const ERANGE = 34; + pub const EDEADLK = 35; + pub const ENAMETOOLONG = 36; + pub const ENOLCK = 37; + pub const ENOSYS = 38; + pub const ENOTEMPTY = 39; + pub const ELOOP = 40; + pub const EWOULDBLOCK = 41; + pub const ENOMSG = 42; + pub const EIDRM = 43; + pub const ECHRNG = 44; + pub const EL2NSYNC = 45; + pub const EL3HLT = 46; + pub const EL3RST = 47; + pub const ELNRNG = 48; + pub const EUNATCH = 49; + pub const ENOCSI = 50; + pub const EL2HLT = 51; + pub const EBADE = 52; + pub const EBADR = 53; + pub const EXFULL = 54; + pub const ENOANO = 55; + pub const EBADRQC = 56; + pub const EBADSLT = 57; + pub const EDEADLOCK = 58; + pub const EBFONT = 59; + pub const ENOSTR = 60; + pub const ENODATA = 61; + pub const ETIME = 62; + pub const ENOSR = 63; + pub const ENONET = 64; + pub const ENOPKG = 65; + pub const EREMOTE = 66; + pub const ENOLINK = 67; + pub const EADV = 68; + pub const ESRMNT = 69; + pub const ECOMM = 70; + pub const EPROTO = 71; + pub const EMULTIHOP = 72; + pub const EDOTDOT = 73; + pub const EBADMSG = 74; + pub const EOVERFLOW = 75; + pub const ENOTUNIQ = 76; + pub const EBADFD = 77; + pub const EREMCHG = 78; + pub const ELIBACC = 79; + pub const ELIBBAD = 80; + pub const ELIBSCN = 81; + pub const ELIBMAX = 82; + pub const ELIBEXEC = 83; + pub const EILSEQ = 84; + pub const ERESTART = 85; + pub const ESTRPIPE = 86; + pub const EUSERS = 87; + pub const ENOTSOCK = 88; + pub const EDESTADDRREQ = 89; + pub const EMSGSIZE = 90; + pub const EPROTOTYPE = 91; + pub const ENOPROTOOPT = 92; + pub const EPROTONOSUPPORT = 93; + pub const ESOCKTNOSUPPORT = 94; + /// For Linux, EOPNOTSUPP is the real value + /// but it's ~the same and is incompatible across operating systems + /// https://lists.gnu.org/archive/html/bug-glibc/2002-08/msg00017.html + pub const ENOTSUP = 95; + pub const EPFNOSUPPORT = 96; + pub const EAFNOSUPPORT = 97; pub const EADDRINUSE = 98; pub const EADDRNOTAVAIL = 99; - pub const EAFNOSUPPORT = 97; - pub const EINPROGRESS = 115; - pub const EALREADY = 114; - pub const ECONNREFUSED = 111; + pub const ENETDOWN = 100; + pub const ENETUNREACH = 101; + pub const ENETRESET = 102; + pub const ECONNABORTED = 103; pub const ECONNRESET = 104; + pub const ENOBUFS = 105; pub const EISCONN = 106; - pub const ENETUNREACH = 101; - pub const ENOENT = 2; - pub const EPROTOTYPE = 91; - pub const ETIMEDOUT = 110; - pub const EROFS = 30; - pub const EISDIR = 21; - pub const ENXIO = 6; - pub const EOVERFLOW = 84; - pub const ESPIPE = 29; pub const ENOTCONN = 107; - pub const EDESTADDRREQ = 89; - pub const EMSGSIZE = 90; - pub const EPIPE = 32; + pub const ESHUTDOWN = 108; + pub const ETOOMANYREFS = 109; + pub const ETIMEDOUT = 110; + pub const ECONNREFUSED = 111; + pub const EHOSTDOWN = 112; + pub const EHOSTUNREACH = 113; + pub const EALREADY = 114; + pub const EINPROGRESS = 115; + pub const ESTALE = 116; + pub const EUCLEAN = 117; + pub const ENOTNAM = 118; + pub const ENAVAIL = 119; + pub const EISNAM = 120; + pub const EREMOTEIO = 121; + pub const EDQUOT = 122; + pub const ENOMEDIUM = 123; + pub const EMEDIUMTYPE = 124; pub const ECANCELED = 125; - pub const EFBIG = 27; + pub const ENOKEY = 126; + pub const EKEYEXPIRED = 127; + pub const EKEYREVOKED = 128; + pub const EKEYREJECTED = 129; + pub const EOWNERDEAD = 130; + pub const ENOTRECOVERABLE = 131; + pub const ERFKILL = 132; + pub const EHWPOISON = 133; +}; + +pub const Errno = error{ + EPERM, + ENOENT, + ESRCH, + EINTR, + EIO, + ENXIO, + E2BIG, + ENOEXEC, + EBADF, + ECHILD, + EAGAIN, + ENOMEM, + EACCES, + EFAULT, + ENOTBLK, + EBUSY, + EEXIST, + EXDEV, + ENODEV, + ENOTDIR, + EISDIR, + EINVAL, + ENFILE, + EMFILE, + ENOTTY, + ETXTBSY, + EFBIG, + ENOSPC, + ESPIPE, + EROFS, + EMLINK, + EPIPE, + EDOM, + ERANGE, + EDEADLK, + ENAMETOOLONG, + ENOLCK, + ENOSYS, + ENOTEMPTY, + ELOOP, + EWOULDBLOCK, + ENOMSG, + EIDRM, + ECHRNG, + EL2NSYNC, + EL3HLT, + EL3RST, + ELNRNG, + EUNATCH, + ENOCSI, + EL2HLT, + EBADE, + EBADR, + EXFULL, + ENOANO, + EBADRQC, + EBADSLT, + EDEADLOCK, + EBFONT, + ENOSTR, + ENODATA, + ETIME, + ENOSR, + ENONET, + ENOPKG, + EREMOTE, + ENOLINK, + EADV, + ESRMNT, + ECOMM, + EPROTO, + EMULTIHOP, + EDOTDOT, + EBADMSG, + EOVERFLOW, + ENOTUNIQ, + EBADFD, + EREMCHG, + ELIBACC, + ELIBBAD, + ELIBSCN, + ELIBMAX, + ELIBEXEC, + EILSEQ, + ERESTART, + ESTRPIPE, + EUSERS, + ENOTSOCK, + EDESTADDRREQ, + EMSGSIZE, + EPROTOTYPE, + ENOPROTOOPT, + EPROTONOSUPPORT, + ESOCKTNOSUPPORT, + ENOTSUP, + EPFNOSUPPORT, + EAFNOSUPPORT, + EADDRINUSE, + EADDRNOTAVAIL, + ENETDOWN, + ENETUNREACH, + ENETRESET, + ECONNABORTED, + ECONNRESET, + ENOBUFS, + EISCONN, + ENOTCONN, + ESHUTDOWN, + ETOOMANYREFS, + ETIMEDOUT, + ECONNREFUSED, + EHOSTDOWN, + EHOSTUNREACH, + EALREADY, + EINPROGRESS, + ESTALE, + EUCLEAN, + ENOTNAM, + ENAVAIL, + EISNAM, + EREMOTEIO, + EDQUOT, + ENOMEDIUM, + EMEDIUMTYPE, + ECANCELED, + ENOKEY, + EKEYEXPIRED, + EKEYREVOKED, + EKEYREJECTED, + EOWNERDEAD, + ENOTRECOVERABLE, + ERFKILL, + EHWPOISON, + Unexpected, +}; +const errno_error_map: [135]Errno = brk: { + var errors: [135]Errno = undefined; + errors[0] = error.Unexpected; + errors[1] = error.EPERM; + errors[2] = error.ENOENT; + errors[3] = error.ESRCH; + errors[4] = error.EINTR; + errors[5] = error.EIO; + errors[6] = error.ENXIO; + errors[7] = error.E2BIG; + errors[8] = error.ENOEXEC; + errors[9] = error.EBADF; + errors[10] = error.ECHILD; + errors[11] = error.EAGAIN; + errors[12] = error.ENOMEM; + errors[13] = error.EACCES; + errors[14] = error.EFAULT; + errors[15] = error.ENOTBLK; + errors[16] = error.EBUSY; + errors[17] = error.EEXIST; + errors[18] = error.EXDEV; + errors[19] = error.ENODEV; + errors[20] = error.ENOTDIR; + errors[21] = error.EISDIR; + errors[22] = error.EINVAL; + errors[23] = error.ENFILE; + errors[24] = error.EMFILE; + errors[25] = error.ENOTTY; + errors[26] = error.ETXTBSY; + errors[27] = error.EFBIG; + errors[28] = error.ENOSPC; + errors[29] = error.ESPIPE; + errors[30] = error.EROFS; + errors[31] = error.EMLINK; + errors[32] = error.EPIPE; + errors[33] = error.EDOM; + errors[34] = error.ERANGE; + errors[35] = error.EDEADLK; + errors[36] = error.ENAMETOOLONG; + errors[37] = error.ENOLCK; + errors[38] = error.ENOSYS; + errors[39] = error.ENOTEMPTY; + errors[40] = error.ELOOP; + errors[41] = error.EWOULDBLOCK; + errors[42] = error.ENOMSG; + errors[43] = error.EIDRM; + errors[44] = error.ECHRNG; + errors[45] = error.EL2NSYNC; + errors[46] = error.EL3HLT; + errors[47] = error.EL3RST; + errors[48] = error.ELNRNG; + errors[49] = error.EUNATCH; + errors[50] = error.ENOCSI; + errors[51] = error.EL2HLT; + errors[52] = error.EBADE; + errors[53] = error.EBADR; + errors[54] = error.EXFULL; + errors[55] = error.ENOANO; + errors[56] = error.EBADRQC; + errors[57] = error.EBADSLT; + errors[58] = error.EDEADLOCK; + errors[59] = error.EBFONT; + errors[60] = error.ENOSTR; + errors[61] = error.ENODATA; + errors[62] = error.ETIME; + errors[63] = error.ENOSR; + errors[64] = error.ENONET; + errors[65] = error.ENOPKG; + errors[66] = error.EREMOTE; + errors[67] = error.ENOLINK; + errors[68] = error.EADV; + errors[69] = error.ESRMNT; + errors[70] = error.ECOMM; + errors[71] = error.EPROTO; + errors[72] = error.EMULTIHOP; + errors[73] = error.EDOTDOT; + errors[74] = error.EBADMSG; + errors[75] = error.EOVERFLOW; + errors[76] = error.ENOTUNIQ; + errors[77] = error.EBADFD; + errors[78] = error.EREMCHG; + errors[79] = error.ELIBACC; + errors[80] = error.ELIBBAD; + errors[81] = error.ELIBSCN; + errors[82] = error.ELIBMAX; + errors[83] = error.ELIBEXEC; + errors[84] = error.EILSEQ; + errors[85] = error.ERESTART; + errors[86] = error.ESTRPIPE; + errors[87] = error.EUSERS; + errors[88] = error.ENOTSOCK; + errors[89] = error.EDESTADDRREQ; + errors[90] = error.EMSGSIZE; + errors[91] = error.EPROTOTYPE; + errors[92] = error.ENOPROTOOPT; + errors[93] = error.EPROTONOSUPPORT; + errors[94] = error.ESOCKTNOSUPPORT; + errors[95] = error.ENOTSUP; + errors[96] = error.EPFNOSUPPORT; + errors[97] = error.EAFNOSUPPORT; + errors[98] = error.EADDRINUSE; + errors[99] = error.EADDRNOTAVAIL; + errors[100] = error.ENETDOWN; + errors[101] = error.ENETUNREACH; + errors[102] = error.ENETRESET; + errors[103] = error.ECONNABORTED; + errors[104] = error.ECONNRESET; + errors[105] = error.ENOBUFS; + errors[106] = error.EISCONN; + errors[107] = error.ENOTCONN; + errors[108] = error.ESHUTDOWN; + errors[109] = error.ETOOMANYREFS; + errors[110] = error.ETIMEDOUT; + errors[111] = error.ECONNREFUSED; + errors[112] = error.EHOSTDOWN; + errors[113] = error.EHOSTUNREACH; + errors[114] = error.EALREADY; + errors[115] = error.EINPROGRESS; + errors[116] = error.ESTALE; + errors[117] = error.EUCLEAN; + errors[118] = error.ENOTNAM; + errors[119] = error.ENAVAIL; + errors[120] = error.EISNAM; + errors[121] = error.EREMOTEIO; + errors[122] = error.EDQUOT; + errors[123] = error.ENOMEDIUM; + errors[124] = error.EMEDIUMTYPE; + errors[125] = error.ECANCELED; + errors[126] = error.ENOKEY; + errors[127] = error.EKEYEXPIRED; + errors[128] = error.EKEYREVOKED; + errors[129] = error.EKEYREJECTED; + errors[130] = error.EOWNERDEAD; + errors[131] = error.ENOTRECOVERABLE; + errors[132] = error.ERFKILL; + errors[133] = error.EHWPOISON; + errors[134] = error.Unexpected; + break :brk errors; }; +pub fn asError(err: anytype) Errno { + return switch (err) { + 1...errno_error_map.len => errno_error_map[@intCast(u8, err)], + else => error.Unexpected, + }; +} + const timespec = std.os.system.timespec; const linux = os.linux; const IO_Uring = linux.IO_Uring; @@ -315,7 +692,7 @@ pub const Completion = struct { os.EOPNOTSUPP => error.OperationNotSupported, os.EPERM => error.PermissionDenied, os.EPROTO => error.ProtocolFailure, - else => error.Unexpected, + else => |errno| asError(errno), } else @intCast(os.socket_t, completion.result); completion.callback(completion.context, completion, &result); }, @@ -326,7 +703,7 @@ pub const Completion = struct { os.EDQUOT => error.DiskQuota, os.EIO => error.InputOutput, os.ENOSPC => error.NoSpaceLeft, - else => error.Unexpected, + else => |errno| asError(errno), } else assert(completion.result == 0); completion.callback(completion.context, completion, &result); }, @@ -353,7 +730,7 @@ pub const Completion = struct { os.EPERM => error.PermissionDenied, os.EPROTOTYPE => error.ProtocolNotSupported, os.ETIMEDOUT => error.ConnectionTimedOut, - else => error.Unexpected, + else => |errno| asError(errno), } else assert(completion.result == 0); completion.callback(completion.context, completion, &result); }, @@ -369,7 +746,7 @@ pub const Completion = struct { os.EIO => error.InputOutput, os.ENOSPC => error.NoSpaceLeft, os.EROFS => error.ReadOnlyFileSystem, - else => error.Unexpected, + else => |errno| asError(errno), } else assert(completion.result == 0); completion.callback(completion.context, completion, &result); }, @@ -391,7 +768,7 @@ pub const Completion = struct { os.ENXIO => error.Unseekable, os.EOVERFLOW => error.Unseekable, os.ESPIPE => error.Unseekable, - else => error.Unexpected, + else => |errno| asError(errno), } else @intCast(usize, completion.result); completion.callback(completion.context, completion, &result); }, @@ -410,7 +787,7 @@ pub const Completion = struct { os.ENOTCONN => error.SocketNotConnected, os.ENOTSOCK => error.FileDescriptorNotASocket, os.ECONNRESET => error.ConnectionResetByPeer, - else => error.Unexpected, + else => |errno| asError(errno), } else @intCast(usize, completion.result); completion.callback(completion.context, completion, &result); }, @@ -437,7 +814,7 @@ pub const Completion = struct { os.ENOTSOCK => error.FileDescriptorNotASocket, os.EOPNOTSUPP => error.OperationNotSupported, os.EPIPE => error.BrokenPipe, - else => error.Unexpected, + else => |errno| asError(errno), } else @intCast(usize, completion.result); completion.callback(completion.context, completion, &result); }, @@ -449,7 +826,7 @@ pub const Completion = struct { }, os.ECANCELED => error.Canceled, os.ETIME => {}, // A success. - else => error.Unexpected, + else => |errno| asError(errno), } else unreachable; completion.callback(completion.context, completion, &result); }, @@ -473,7 +850,7 @@ pub const Completion = struct { os.EPERM => error.AccessDenied, os.EPIPE => error.BrokenPipe, os.ESPIPE => error.Unseekable, - else => error.Unexpected, + else => |errno| asError(errno), } else @intCast(usize, completion.result); completion.callback(completion.context, completion, &result); }, @@ -533,7 +910,7 @@ pub const AcceptError = error{ OperationNotSupported, PermissionDenied, ProtocolFailure, -} || os.UnexpectedError; +} || Errno; pub fn accept( self: *IO, @@ -575,7 +952,7 @@ pub const CloseError = error{ DiskQuota, InputOutput, NoSpaceLeft, -} || os.UnexpectedError; +} || Errno; pub fn close( self: *IO, @@ -624,7 +1001,7 @@ pub const ConnectError = error{ PermissionDenied, ProtocolNotSupported, ConnectionTimedOut, -} || os.UnexpectedError; +} || Errno; pub fn connect( self: *IO, @@ -668,7 +1045,7 @@ pub const FsyncError = error{ InputOutput, NoSpaceLeft, ReadOnlyFileSystem, -} || os.UnexpectedError; +} || Errno; pub fn fsync( self: *IO, @@ -712,7 +1089,7 @@ pub const ReadError = error{ IsDir, SystemResources, Unseekable, -} || os.UnexpectedError; +} || Errno; pub fn read( self: *IO, @@ -758,7 +1135,7 @@ pub const RecvError = error{ SystemResources, SocketNotConnected, FileDescriptorNotASocket, -} || os.UnexpectedError; +} || Errno; pub fn recv( self: *IO, @@ -808,7 +1185,7 @@ pub const SendError = error{ FileDescriptorNotASocket, OperationNotSupported, BrokenPipe, -} || os.UnexpectedError; +} || Errno; pub fn send( self: *IO, @@ -846,7 +1223,7 @@ pub fn send( self.enqueue(completion); } -pub const TimeoutError = error{Canceled} || os.UnexpectedError; +pub const TimeoutError = error{Canceled} || Errno; pub fn timeout( self: *IO, @@ -893,7 +1270,7 @@ pub const WriteError = error{ Unseekable, AccessDenied, BrokenPipe, -} || os.UnexpectedError; +} || Errno; pub fn write( self: *IO, |