zig/lib/std/posix.zig
Ryan Liptak 29b7214027 Disentangle from error.CurrentWorkingDirectoryUnlinked
This error is actually only ever directly returned from `std.posix.getcwd` (and only on POSIX systems, so never on Windows). Its inclusion in almost all of the error sets its currently found in is a leftover from when `std.fs.path.resolve` called `std.process.getCwdAlloc` (https://github.com/ziglang/zig/issues/13613).
2026-01-27 20:48:25 +01:00

2232 lines
79 KiB
Zig
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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
//! `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;
const page_size_min = std.heap.page_size_min;
test {
_ = @import("posix/test.zig");
}
/// Whether to use libc for the POSIX API layer.
const use_libc = builtin.link_libc or switch (native_os) {
.windows, .wasi => true,
else => false,
};
const linux = std.os.linux;
const windows = std.os.windows;
const wasi = std.os.wasi;
/// A libc-compatible API layer.
pub const system = if (use_libc)
std.c
else switch (native_os) {
.linux => linux,
.plan9 => std.os.plan9,
else => struct {
pub const pid_t = void;
pub const pollfd = void;
pub const fd_t = void;
pub const uid_t = void;
pub const gid_t = void;
pub const mode_t = u0;
pub const nlink_t = u0;
pub const blksize_t = void;
pub const ino_t = void;
pub const IFNAMESIZE = {};
pub const SIG = void;
},
};
pub const AF = system.AF;
pub const AF_SUN = system.AF_SUN;
pub const AI = system.AI;
pub const ARCH = system.ARCH;
pub const AT = system.AT;
pub const AT_SUN = system.AT_SUN;
pub const CLOCK = system.CLOCK;
pub const CPU_COUNT = system.CPU_COUNT;
pub const CTL = system.CTL;
pub const DT = system.DT;
pub const E = system.E;
pub const Elf_Symndx = system.Elf_Symndx;
pub const F = system.F;
pub const FD_CLOEXEC = system.FD_CLOEXEC;
pub const Flock = system.Flock;
pub const HOST_NAME_MAX = system.HOST_NAME_MAX;
pub const HW = system.HW;
pub const IFNAMESIZE = system.IFNAMESIZE;
pub const IOV_MAX = system.IOV_MAX;
pub const IP = system.IP;
pub const IPV6 = system.IPV6;
pub const IPPROTO = system.IPPROTO;
pub const IPTOS = system.IPTOS;
pub const KERN = system.KERN;
pub const Kevent = system.Kevent;
pub const MADV = system.MADV;
pub const MAP = system.MAP;
pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;
pub const MCL = system.MCL;
pub const MFD = system.MFD;
pub const MLOCK = system.MLOCK;
pub const MREMAP = system.MREMAP;
pub const MSF = system.MSF;
pub const MSG = system.MSG;
pub const NAME_MAX = system.NAME_MAX;
pub const NSIG = system.NSIG;
pub const O = system.O;
pub const PATH_MAX = system.PATH_MAX;
pub const POLL = system.POLL;
pub const POSIX_FADV = system.POSIX_FADV;
pub const PR = system.PR;
pub const PROT = system.PROT;
pub const RLIM = system.RLIM;
pub const S = system.S;
pub const SA = system.SA;
pub const SC = system.SC;
pub const SCM = system.SCM;
pub const SEEK = system.SEEK;
pub const SHUT = system.SHUT;
pub const SIG = system.SIG;
pub const SIOCGIFINDEX = system.SIOCGIFINDEX;
pub const SO = system.SO;
pub const SOCK = system.SOCK;
pub const SOL = system.SOL;
pub const IFF = system.IFF;
pub const STDERR_FILENO = system.STDERR_FILENO;
pub const STDIN_FILENO = system.STDIN_FILENO;
pub const STDOUT_FILENO = system.STDOUT_FILENO;
pub const SYS = system.SYS;
pub const Sigaction = system.Sigaction;
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,
};
pub const T = system.T;
pub const TCP = system.TCP;
pub const VDSO = system.VDSO;
pub const W = system.W;
pub const _SC = system._SC;
pub const addrinfo = system.addrinfo;
pub const blkcnt_t = system.blkcnt_t;
pub const blksize_t = system.blksize_t;
pub const clock_t = system.clock_t;
pub const clockid_t = system.clockid_t;
pub const timerfd_clockid_t = system.timerfd_clockid_t;
pub const cpu_set_t = system.cpu_set_t;
pub const dev_t = system.dev_t;
pub const dl_phdr_info = system.dl_phdr_info;
pub const fd_t = system.fd_t;
pub const file_obj = system.file_obj;
pub const gid_t = system.gid_t;
pub const ifreq = system.ifreq;
pub const in_pktinfo = system.in_pktinfo;
pub const in6_pktinfo = system.in6_pktinfo;
pub const ino_t = system.ino_t;
pub const linger = system.linger;
pub const mode_t = system.mode_t;
pub const msghdr = system.msghdr;
pub const msghdr_const = system.msghdr_const;
pub const nfds_t = system.nfds_t;
pub const nlink_t = system.nlink_t;
pub const off_t = system.off_t;
pub const pid_t = system.pid_t;
pub const pollfd = system.pollfd;
pub const port_event = system.port_event;
pub const port_notify = system.port_notify;
pub const port_t = system.port_t;
pub const rlim_t = system.rlim_t;
pub const rlimit = system.rlimit;
pub const rlimit_resource = system.rlimit_resource;
pub const rusage = system.rusage;
pub const sa_family_t = system.sa_family_t;
pub const siginfo_t = system.siginfo_t;
pub const sigset_t = system.sigset_t;
pub const sigrtmin = system.sigrtmin;
pub const sigrtmax = system.sigrtmax;
pub const sockaddr = system.sockaddr;
pub const socklen_t = system.socklen_t;
pub const stack_t = system.stack_t;
pub const time_t = system.time_t;
pub const timespec = system.timespec;
pub const timestamp_t = system.timestamp_t;
pub const timeval = system.timeval;
pub const timezone = system.timezone;
pub const uid_t = system.uid_t;
pub const user_desc = system.user_desc;
pub const utsname = system.utsname;
pub const termios = system.termios;
pub const CSIZE = system.CSIZE;
pub const NCCS = system.NCCS;
pub const cc_t = system.cc_t;
pub const V = system.V;
pub const speed_t = system.speed_t;
pub const tc_iflag_t = system.tc_iflag_t;
pub const tc_oflag_t = system.tc_oflag_t;
pub const tc_cflag_t = system.tc_cflag_t;
pub const tc_lflag_t = system.tc_lflag_t;
pub const F_OK = system.F_OK;
pub const R_OK = system.R_OK;
pub const W_OK = system.W_OK;
pub const X_OK = system.X_OK;
pub const iovec = extern struct {
base: [*]u8,
len: usize,
};
pub const iovec_const = extern struct {
base: [*]const u8,
len: usize,
};
pub const ACCMODE = switch (native_os) {
// POSIX has a note about the access mode values:
//
// In historical implementations the value of O_RDONLY is zero. Because of
// that, it is not possible to detect the presence of O_RDONLY and another
// option. Future implementations should encode O_RDONLY and O_WRONLY as
// bit flags so that: O_RDONLY | O_WRONLY == O_RDWR
//
// In practice SerenityOS is the only system supported by Zig that
// implements this suggestion.
// https://github.com/SerenityOS/serenity/blob/4adc51fdf6af7d50679c48b39362e062f5a3b2cb/Kernel/API/POSIX/fcntl.h#L28-L30
.serenity => enum(u2) {
NONE = 0,
RDONLY = 1,
WRONLY = 2,
RDWR = 3,
},
else => enum(u2) {
RDONLY = 0,
WRONLY = 1,
RDWR = 2,
},
};
pub const TCSA = enum(c_uint) {
NOW,
DRAIN,
FLUSH,
_,
};
pub const winsize = extern struct {
row: u16,
col: u16,
xpixel: u16,
ypixel: u16,
};
pub const LOCK = struct {
pub const SH = 1;
pub const EX = 2;
pub const NB = 4;
pub const UN = 8;
};
pub const LOG = struct {
/// system is unusable
pub const EMERG = 0;
/// action must be taken immediately
pub const ALERT = 1;
/// critical conditions
pub const CRIT = 2;
/// error conditions
pub const ERR = 3;
/// warning conditions
pub const WARNING = 4;
/// normal but significant condition
pub const NOTICE = 5;
/// informational
pub const INFO = 6;
/// debug-level messages
pub const DEBUG = 7;
};
pub const socket_t = if (native_os == .windows) windows.ws2_32.SOCKET else fd_t;
/// Obtains errno from the return value of a system function call.
///
/// For some systems this will obtain the value directly from the syscall return value;
/// for others it will use a thread-local errno variable. Therefore, this
/// function only returns a well-defined value when it is called directly after
/// the system function call whose errno value is intended to be observed.
pub const errno = system.errno;
/// Closes the file descriptor.
///
/// Asserts the file descriptor is open.
///
/// This function is not capable of returning any indication of failure. An
/// application which wants to ensure writes have succeeded before closing must
/// call `fsync` before `close`.
///
/// The Zig standard library does not support POSIX thread cancellation.
pub fn close(fd: fd_t) void {
if (native_os == .windows) {
return windows.CloseHandle(fd);
}
if (native_os == .wasi and !builtin.link_libc) {
_ = std.os.wasi.fd_close(fd);
return;
}
switch (errno(system.close(fd))) {
.BADF => unreachable, // Always a race condition.
.INTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
else => return,
}
}
pub const RebootError = error{
PermissionDenied,
} || UnexpectedError;
pub const RebootCommand = switch (native_os) {
.linux => union(linux.LINUX_REBOOT.CMD) {
RESTART: void,
HALT: void,
CAD_ON: void,
CAD_OFF: void,
POWER_OFF: void,
RESTART2: [*:0]const u8,
SW_SUSPEND: void,
KEXEC: void,
},
else => @compileError("Unsupported OS"),
};
pub fn reboot(cmd: RebootCommand) RebootError!void {
switch (native_os) {
.linux => {
switch (linux.errno(linux.reboot(
.MAGIC1,
.MAGIC2,
cmd,
switch (cmd) {
.RESTART2 => |s| s,
else => null,
},
))) {
.SUCCESS => {},
.PERM => return error.PermissionDenied,
else => |err| return std.posix.unexpectedErrno(err),
}
switch (cmd) {
.CAD_OFF => {},
.CAD_ON => {},
.SW_SUSPEND => {},
.HALT => unreachable,
.KEXEC => unreachable,
.POWER_OFF => unreachable,
.RESTART => unreachable,
.RESTART2 => unreachable,
}
},
else => @compileError("Unsupported OS"),
}
}
pub const RaiseError = UnexpectedError;
pub fn raise(sig: SIG) RaiseError!void {
if (builtin.link_libc) {
switch (errno(system.raise(sig))) {
.SUCCESS => return,
else => |err| return unexpectedErrno(err),
}
}
if (native_os == .linux) {
// Block all signals so a `fork` (from a signal handler) between the gettid() and kill() syscalls
// cannot trigger an extra, unexpected, inter-process signal. Signal paranoia inherited from Musl.
const filled = linux.sigfillset();
var orig: sigset_t = undefined;
sigprocmask(SIG.BLOCK, &filled, &orig);
const rc = linux.tkill(linux.gettid(), sig);
sigprocmask(SIG.SETMASK, &orig, null);
switch (errno(rc)) {
.SUCCESS => return,
else => |err| return unexpectedErrno(err),
}
}
@compileError("std.posix.raise unimplemented for this target");
}
pub const KillError = error{ ProcessNotFound, PermissionDenied } || UnexpectedError;
pub fn kill(pid: pid_t, sig: SIG) KillError!void {
switch (errno(system.kill(pid, sig))) {
.SUCCESS => return,
.INVAL => unreachable, // invalid signal
.PERM => return error.PermissionDenied,
.SRCH => return error.ProcessNotFound,
else => |err| return unexpectedErrno(err),
}
}
pub const ReadError = std.Io.File.Reader.Error;
/// Returns the number of bytes that were read, which can be less than
/// buf.len. If 0 bytes were read, that means EOF.
/// If `fd` is opened in non blocking mode, the function will return error.WouldBlock
/// when EAGAIN is received.
///
/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
/// The corresponding POSIX limit is `maxInt(isize)`.
pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
if (buf.len == 0) return 0;
if (native_os == .windows) @compileError("unsupported OS");
if (native_os == .wasi) @compileError("unsupported OS");
// Prevents EINVAL.
const max_count = switch (native_os) {
.linux => 0x7ffff000,
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => maxInt(i32),
else => maxInt(isize),
};
while (true) {
const rc = system.read(fd, buf.ptr, @min(buf.len, max_count));
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.INTR => continue,
.INVAL => unreachable,
.FAULT => unreachable,
.AGAIN => return error.WouldBlock,
.CANCELED => return error.Canceled,
.BADF => return error.Unexpected, // use after free
.IO => return error.InputOutput,
.ISDIR => return error.IsDir,
.NOBUFS => return error.SystemResources,
.NOMEM => return error.SystemResources,
.NOTCONN => return error.SocketUnconnected,
.CONNRESET => return error.ConnectionResetByPeer,
.TIMEDOUT => return error.Unexpected,
else => |err| return unexpectedErrno(err),
}
}
}
pub const OpenError = std.Io.File.OpenError || error{WouldBlock};
/// Open and possibly create a file. Keeps trying if it gets interrupted.
/// `file_path` is relative to the open directory handle `dir_fd`.
/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
/// On WASI, `file_path` should be encoded as valid UTF-8.
/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
/// See also `openatZ`.
pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: O, mode: mode_t) OpenError!fd_t {
if (native_os == .windows) {
@compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
} else if (native_os == .wasi and !builtin.link_libc) {
@compileError("use std.Io instead");
}
const file_path_c = try toPosixPath(file_path);
return openatZ(dir_fd, &file_path_c, flags, mode);
}
/// Open and possibly create a file. Keeps trying if it gets interrupted.
/// `file_path` is relative to the open directory handle `dir_fd`.
/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
/// On WASI, `file_path` should be encoded as valid UTF-8.
/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
/// See also `openat`.
pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) OpenError!fd_t {
if (native_os == .windows) {
@compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
} else if (native_os == .wasi and !builtin.link_libc) {
return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode);
}
const openat_sym = if (lfs64_abi) system.openat64 else system.openat;
while (true) {
const rc = openat_sym(dir_fd, file_path, flags, mode);
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.INTR => continue,
.FAULT => unreachable,
.INVAL => return error.BadPathName,
.BADF => unreachable,
.ACCES => return error.AccessDenied,
.FBIG => return error.FileTooBig,
.OVERFLOW => return error.FileTooBig,
.ISDIR => return error.IsDir,
.LOOP => return error.SymLinkLoop,
.MFILE => return error.ProcessFdQuotaExceeded,
.NAMETOOLONG => return error.NameTooLong,
.NFILE => return error.SystemFdQuotaExceeded,
.NODEV => return error.NoDevice,
.NOENT => return error.FileNotFound,
.SRCH => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOSPC => return error.NoSpaceLeft,
.NOTDIR => return error.NotDir,
.PERM => return error.PermissionDenied,
.EXIST => return error.PathAlreadyExists,
.BUSY => return error.DeviceBusy,
.OPNOTSUPP => return error.FileLocksUnsupported,
.AGAIN => return error.WouldBlock,
.TXTBSY => return error.FileBusy,
.NXIO => return error.NoDevice,
.ILSEQ => return error.BadPathName,
else => |err| return unexpectedErrno(err),
}
}
}
pub fn getppid() pid_t {
return system.getppid();
}
pub const GetCwdError = error{
NameTooLong,
/// Not possible on Windows.
CurrentWorkingDirectoryUnlinked,
} || UnexpectedError;
/// The result is a slice of out_buffer, indexed from 0.
pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
if (native_os == .windows) {
return windows.GetCurrentDirectory(out_buffer);
} else if (native_os == .wasi and !builtin.link_libc) {
const path = ".";
if (out_buffer.len < path.len) return error.NameTooLong;
const result = out_buffer[0..path.len];
@memcpy(result, path);
return result;
}
const err: E = if (builtin.link_libc) err: {
const c_err = if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;
break :err @enumFromInt(c_err);
} else err: {
break :err errno(system.getcwd(out_buffer.ptr, out_buffer.len));
};
switch (err) {
.SUCCESS => return mem.sliceTo(out_buffer, 0),
.FAULT => unreachable,
.INVAL => unreachable,
.NOENT => return error.CurrentWorkingDirectoryUnlinked,
.RANGE => return error.NameTooLong,
else => return unexpectedErrno(err),
}
}
pub const SocketError = error{
/// Permission to create a socket of the specified type and/or
/// protocol 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,
/// The network subsystem has failed.
NetworkDown,
/// Socket hasn't been bound yet
SocketNotBound,
FileDescriptorNotASocket,
} || UnexpectedError;
pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
if (native_os == .windows) {
const rc = windows.getpeername(sock, addr, addrlen);
if (rc == windows.ws2_32.SOCKET_ERROR) {
switch (windows.ws2_32.WSAGetLastError()) {
.NOTINITIALISED => unreachable,
.ENETDOWN => return error.NetworkDown,
.EFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
.ENOTSOCK => return error.FileDescriptorNotASocket,
.EINVAL => return error.SocketNotBound,
else => |err| return windows.unexpectedWSAError(err),
}
}
return;
} else {
const rc = system.getpeername(sock, addr, addrlen);
switch (errno(rc)) {
.SUCCESS => return,
else => |err| return unexpectedErrno(err),
.BADF => unreachable, // always a race condition
.FAULT => unreachable,
.INVAL => unreachable, // invalid parameters
.NOTSOCK => return error.FileDescriptorNotASocket,
.NOBUFS => return error.SystemResources,
}
}
}
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,
SystemResources,
PermissionDenied,
/// The kernel does not recognize the flags passed, likely because it is an
/// older version.
UnsupportedFlags,
} || UnexpectedError;
pub fn fanotify_init(flags: std.os.linux.fanotify.InitFlags, event_f_flags: u32) FanotifyInitError!i32 {
const rc = system.fanotify_init(flags, event_f_flags);
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.INVAL => return error.UnsupportedFlags,
.MFILE => return error.ProcessFdQuotaExceeded,
.NFILE => return error.SystemFdQuotaExceeded,
.NOMEM => return error.SystemResources,
.PERM => return error.PermissionDenied,
else => |err| return unexpectedErrno(err),
}
}
pub const FanotifyMarkError = error{
MarkAlreadyExists,
IsDir,
NotAssociatedWithFileSystem,
FileNotFound,
SystemResources,
UserMarkQuotaExceeded,
NotDir,
OperationUnsupported,
PermissionDenied,
CrossDevice,
NameTooLong,
} || UnexpectedError;
pub fn fanotify_mark(
fanotify_fd: fd_t,
flags: std.os.linux.fanotify.MarkFlags,
mask: std.os.linux.fanotify.MarkMask,
dirfd: fd_t,
pathname: ?[]const u8,
) FanotifyMarkError!void {
if (pathname) |path| {
const path_c = try toPosixPath(path);
return fanotify_markZ(fanotify_fd, flags, mask, dirfd, &path_c);
} else {
return fanotify_markZ(fanotify_fd, flags, mask, dirfd, null);
}
}
pub fn fanotify_markZ(
fanotify_fd: fd_t,
flags: std.os.linux.fanotify.MarkFlags,
mask: std.os.linux.fanotify.MarkMask,
dirfd: fd_t,
pathname: ?[*:0]const u8,
) FanotifyMarkError!void {
const rc = system.fanotify_mark(fanotify_fd, flags, mask, dirfd, pathname);
switch (errno(rc)) {
.SUCCESS => return,
.BADF => unreachable,
.EXIST => return error.MarkAlreadyExists,
.INVAL => unreachable,
.ISDIR => return error.IsDir,
.NODEV => return error.NotAssociatedWithFileSystem,
.NOENT => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOSPC => return error.UserMarkQuotaExceeded,
.NOTDIR => return error.NotDir,
.OPNOTSUPP => return error.OperationUnsupported,
.PERM => return error.PermissionDenied,
.XDEV => return error.CrossDevice,
else => |err| return unexpectedErrno(err),
}
}
pub const MMapError = error{
/// The underlying filesystem of the specified file does not support memory mapping.
MemoryMappingNotSupported,
/// A file descriptor refers to a non-regular file. Or a file mapping was requested,
/// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested
/// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode.
/// Or `PROT_WRITE` is set, but the file is append-only.
AccessDenied,
/// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
/// a filesystem that was mounted no-exec.
PermissionDenied,
LockedMemoryLimitExceeded,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
OutOfMemory,
/// Using FIXED_NOREPLACE flag and the process has already mapped memory at the given address
MappingAlreadyExists,
} || UnexpectedError;
/// Map files or devices into memory.
/// `length` does not need to be aligned.
/// Use of a mapped region can result in these signals:
/// * SIGSEGV - Attempted write into a region mapped as read-only.
/// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file
pub fn mmap(
ptr: ?[*]align(page_size_min) u8,
length: usize,
prot: PROT,
flags: MAP,
fd: fd_t,
offset: u64,
) MMapError![]align(page_size_min) u8 {
const mmap_sym = if (lfs64_abi) system.mmap64 else system.mmap;
const rc = mmap_sym(ptr, length, prot, @bitCast(flags), fd, @bitCast(offset));
const err: E = if (builtin.link_libc) blk: {
if (rc != std.c.MAP_FAILED) return @as([*]align(page_size_min) u8, @ptrCast(@alignCast(rc)))[0..length];
break :blk @enumFromInt(system._errno().*);
} else blk: {
const err = errno(rc);
if (err == .SUCCESS) return @as([*]align(page_size_min) u8, @ptrFromInt(rc))[0..length];
break :blk err;
};
switch (err) {
.SUCCESS => unreachable,
.TXTBSY => return error.AccessDenied,
.ACCES => return error.AccessDenied,
.PERM => return error.PermissionDenied,
.AGAIN => return error.LockedMemoryLimitExceeded,
.BADF => unreachable, // Always a race condition.
.OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
.NODEV => return error.MemoryMappingNotSupported,
.INVAL => unreachable, // Invalid parameters to mmap()
.MFILE => return error.ProcessFdQuotaExceeded,
.NFILE => return error.SystemFdQuotaExceeded,
.NOMEM => return error.OutOfMemory,
.EXIST => return error.MappingAlreadyExists,
else => return unexpectedErrno(err),
}
}
/// Deletes the mappings for the specified address range, causing
/// further references to addresses within the range to generate invalid memory references.
/// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
/// Zig's munmap function does not, for two reasons:
/// * It violates the Zig principle that resource deallocation must succeed.
/// * The Windows function, NtFreeVirtualMemory, has this restriction.
pub fn munmap(memory: []align(page_size_min) const u8) void {
switch (errno(system.munmap(memory.ptr, memory.len))) {
.SUCCESS => return,
.INVAL => unreachable, // Invalid parameters.
.NOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
else => |e| if (unexpected_error_tracing) {
std.debug.panic("unexpected errno: {d} ({t})", .{ @intFromEnum(e), e });
} else unreachable,
}
}
pub const MRemapError = error{
LockedMemoryLimitExceeded,
/// Either a bug in the calling code, or the operating system abused the
/// EINVAL error code.
InvalidSyscallParameters,
OutOfMemory,
} || UnexpectedError;
pub fn mremap(
old_address: ?[*]align(page_size_min) u8,
old_len: usize,
new_len: usize,
flags: system.MREMAP,
new_address: ?[*]align(page_size_min) u8,
) MRemapError![]align(page_size_min) u8 {
const rc = system.mremap(old_address, old_len, new_len, flags, new_address);
const err: E = if (builtin.link_libc) blk: {
if (rc != std.c.MAP_FAILED) return @as([*]align(page_size_min) u8, @ptrCast(@alignCast(rc)))[0..new_len];
break :blk @enumFromInt(system._errno().*);
} else blk: {
const err = errno(rc);
if (err == .SUCCESS) return @as([*]align(page_size_min) u8, @ptrFromInt(rc))[0..new_len];
break :blk err;
};
switch (err) {
.SUCCESS => unreachable,
.AGAIN => return error.LockedMemoryLimitExceeded,
.INVAL => return error.InvalidSyscallParameters,
.NOMEM => return error.OutOfMemory,
.FAULT => unreachable,
else => return unexpectedErrno(err),
}
}
pub const MSyncError = error{
UnmappedMemory,
PermissionDenied,
} || UnexpectedError;
pub fn msync(memory: []align(page_size_min) u8, flags: i32) MSyncError!void {
switch (errno(system.msync(memory.ptr, memory.len, flags))) {
.SUCCESS => return,
.PERM => return error.PermissionDenied,
.NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory
.INVAL => unreachable, // Invalid parameters.
else => unreachable,
}
}
pub const SysCtlError = error{
PermissionDenied,
SystemResources,
NameTooLong,
UnknownName,
} || UnexpectedError;
pub fn sysctl(
name: []const c_int,
oldp: ?*anyopaque,
oldlenp: ?*usize,
newp: ?*anyopaque,
newlen: usize,
) SysCtlError!void {
if (native_os == .wasi) {
@compileError("sysctl not supported on WASI");
}
if (native_os == .haiku) {
@compileError("sysctl not supported on Haiku");
}
const name_len = cast(c_uint, name.len) orelse return error.NameTooLong;
switch (errno(system.sysctl(name.ptr, name_len, 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 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));
const phdrs: [*]std.elf.ElfN.Phdr = @ptrFromInt(getauxval(std.elf.AT_PHDR));
return phdrs[0..getauxval(std.elf.AT_PHNUM)];
}
pub fn dl_iterate_phdr(
context: anytype,
comptime Error: type,
comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
) Error!void {
const Context = @TypeOf(context);
const elf = std.elf;
const dl = @import("dynamic_library.zig");
switch (builtin.object_format) {
.elf, .c => {},
else => @compileError("dl_iterate_phdr is not available for this target"),
}
if (builtin.link_libc) {
switch (system.dl_iterate_phdr(struct {
fn callbackC(info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.c) c_int {
const context_ptr: *const Context = @ptrCast(@alignCast(data));
callback(info, size, context_ptr.*) catch |err| return @intFromError(err);
return 0;
}
}.callbackC, @ptrCast(@constCast(&context)))) {
0 => return,
else => |err| return @as(Error, @errorCast(@errorFromInt(@as(std.meta.Int(.unsigned, @bitSizeOf(anyerror)), @intCast(err))))),
}
}
var it = dl.linkmap_iterator() catch unreachable;
// The executable has no dynamic link segment, create a single entry for
// the whole ELF image.
if (it.end()) {
const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval;
const phdrs = getSelfPhdrs();
var info: dl_phdr_info = .{
.addr = for (phdrs) |phdr| switch (phdr.type) {
.PHDR => break @intFromPtr(phdrs.ptr) - phdr.vaddr,
else => {},
} else unreachable,
.name = switch (getauxval(std.elf.AT_EXECFN)) {
0 => "/proc/self/exe",
else => |name| @ptrFromInt(name),
},
.phdr = phdrs.ptr,
.phnum = @intCast(phdrs.len),
};
return callback(&info, @sizeOf(dl_phdr_info), context);
}
// Last return value from the callback function.
while (it.next()) |entry| {
const phdrs: []elf.ElfN.Phdr = if (entry.addr != 0) phdrs: {
const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.addr);
assert(mem.eql(u8, ehdr.ident[0..4], elf.MAGIC));
const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.addr + ehdr.phoff);
break :phdrs phdrs[0..ehdr.phnum];
} else getSelfPhdrs();
var info: dl_phdr_info = .{
.addr = entry.addr,
.name = entry.name,
.phdr = phdrs.ptr,
.phnum = @intCast(phdrs.len),
};
try callback(&info, @sizeOf(dl_phdr_info), context);
}
}
pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
pub fn clock_gettime(clock_id: clockid_t) ClockGetTimeError!timespec {
var tp: timespec = undefined;
if (native_os == .windows) {
@compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.time API");
} else if (native_os == .wasi and !builtin.link_libc) {
var ts: timestamp_t = undefined;
switch (system.clock_time_get(clock_id, 1, &ts)) {
.SUCCESS => {
tp = .{
.sec = @intCast(ts / std.time.ns_per_s),
.nsec = @intCast(ts % std.time.ns_per_s),
};
},
.INVAL => return error.UnsupportedClock,
else => |err| return unexpectedErrno(err),
}
return tp;
}
switch (errno(system.clock_gettime(clock_id, &tp))) {
.SUCCESS => return tp,
.FAULT => unreachable,
.INVAL => return error.UnsupportedClock,
else => |err| return unexpectedErrno(err),
}
}
pub fn clock_getres(clock_id: clockid_t, res: *timespec) ClockGetTimeError!void {
if (native_os == .wasi and !builtin.link_libc) {
var ts: timestamp_t = undefined;
switch (system.clock_res_get(@bitCast(clock_id), &ts)) {
.SUCCESS => res.* = .{
.sec = @intCast(ts / std.time.ns_per_s),
.nsec = @intCast(ts % std.time.ns_per_s),
},
.INVAL => return error.UnsupportedClock,
else => |err| return unexpectedErrno(err),
}
return;
}
switch (errno(system.clock_getres(clock_id, res))) {
.SUCCESS => return,
.FAULT => unreachable,
.INVAL => return error.UnsupportedClock,
else => |err| return unexpectedErrno(err),
}
}
pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
var set: cpu_set_t = undefined;
switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
.SUCCESS => return set,
.FAULT => unreachable,
.INVAL => unreachable,
.SRCH => unreachable,
.PERM => return error.PermissionDenied,
else => |err| return unexpectedErrno(err),
}
}
pub const SigaltstackError = error{
/// The supplied stack size was less than MINSIGSTKSZ.
SizeTooSmall,
/// Attempted to change the signal stack while it was active.
PermissionDenied,
} || UnexpectedError;
pub fn sigaltstack(ss: ?*const stack_t, old_ss: ?*stack_t) SigaltstackError!void {
switch (errno(system.sigaltstack(ss, old_ss))) {
.SUCCESS => return,
.FAULT => unreachable,
.INVAL => unreachable,
.NOMEM => return error.SizeTooSmall,
.PERM => return error.PermissionDenied,
else => |err| return unexpectedErrno(err),
}
}
/// Return a filled sigset_t.
pub fn sigfillset() sigset_t {
if (builtin.link_libc) {
var set: sigset_t = undefined;
switch (errno(system.sigfillset(&set))) {
.SUCCESS => return set,
else => unreachable,
}
}
return system.sigfillset();
}
/// Return an empty sigset_t.
pub fn sigemptyset() sigset_t {
if (builtin.link_libc) {
var set: sigset_t = undefined;
switch (errno(system.sigemptyset(&set))) {
.SUCCESS => return set,
else => unreachable,
}
}
return system.sigemptyset();
}
pub fn sigaddset(set: *sigset_t, sig: SIG) void {
if (builtin.link_libc) {
switch (errno(system.sigaddset(set, sig))) {
.SUCCESS => return,
else => unreachable,
}
}
system.sigaddset(set, sig);
}
pub fn sigdelset(set: *sigset_t, sig: SIG) void {
if (builtin.link_libc) {
switch (errno(system.sigdelset(set, sig))) {
.SUCCESS => return,
else => unreachable,
}
}
system.sigdelset(set, sig);
}
pub fn sigismember(set: *const sigset_t, sig: SIG) bool {
if (builtin.link_libc) {
const rc = system.sigismember(set, sig);
switch (errno(rc)) {
.SUCCESS => return rc == 1,
else => unreachable,
}
}
return system.sigismember(set, sig);
}
/// Examine and change a signal action.
pub fn sigaction(sig: SIG, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) void {
switch (errno(system.sigaction(sig, act, oact))) {
.SUCCESS => return,
// EINVAL means the signal is either invalid or some signal that cannot have its action
// changed. For POSIX, this means SIGKILL/SIGSTOP. For e.g. illumos, this also includes the
// non-standard SIGWAITING, SIGCANCEL, and SIGLWP. Either way, programmer error.
.INVAL => unreachable,
else => unreachable,
}
}
/// Sets the thread signal mask.
pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void {
switch (errno(system.sigprocmask(@bitCast(flags), set, oldset))) {
.SUCCESS => return,
.FAULT => unreachable,
.INVAL => unreachable,
else => unreachable,
}
}
pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;
pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
if (builtin.link_libc) {
switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
.SUCCESS => return mem.sliceTo(name_buffer, 0),
.FAULT => unreachable,
.NAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
.PERM => return error.PermissionDenied,
else => |err| return unexpectedErrno(err),
}
}
if (native_os == .linux) {
const uts = uname();
const hostname = mem.sliceTo(&uts.nodename, 0);
const result = name_buffer[0..hostname.len];
@memcpy(result, hostname);
return result;
}
@compileError("TODO implement gethostname for this OS");
}
pub fn uname() utsname {
var uts: utsname = undefined;
switch (errno(system.uname(&uts))) {
.SUCCESS => return uts,
.FAULT => unreachable,
else => unreachable,
}
}
pub const PollError = error{
/// The network subsystem has failed.
NetworkDown,
/// The kernel had no space to allocate file descriptor tables.
SystemResources,
} || UnexpectedError;
pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
if (native_os == .windows) {
switch (windows.poll(fds.ptr, @intCast(fds.len), timeout)) {
windows.ws2_32.SOCKET_ERROR => switch (windows.ws2_32.WSAGetLastError()) {
.NOTINITIALISED => unreachable,
.ENETDOWN => return error.NetworkDown,
.ENOBUFS => return error.SystemResources,
// TODO: handle more errors
else => |err| return windows.unexpectedWSAError(err),
},
else => |rc| return @intCast(rc),
}
}
while (true) {
const fds_count = cast(nfds_t, fds.len) orelse return error.SystemResources;
const rc = system.poll(fds.ptr, fds_count, timeout);
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.FAULT => unreachable,
.INTR => continue,
.INVAL => unreachable,
.NOMEM => return error.SystemResources,
else => |err| return unexpectedErrno(err),
}
}
unreachable;
}
pub const PPollError = error{
/// The operation was interrupted by a delivery of a signal before it could complete.
SignalInterrupt,
/// The kernel had no space to allocate file descriptor tables.
SystemResources,
} || UnexpectedError;
pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) PPollError!usize {
var ts: timespec = undefined;
var ts_ptr: ?*timespec = null;
if (timeout) |timeout_ns| {
ts_ptr = &ts;
ts = timeout_ns.*;
}
const fds_count = cast(nfds_t, fds.len) orelse return error.SystemResources;
const rc = system.ppoll(fds.ptr, fds_count, ts_ptr, mask);
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.FAULT => unreachable,
.INTR => return error.SignalInterrupt,
.INVAL => unreachable,
.NOMEM => return error.SystemResources,
else => |err| return unexpectedErrno(err),
}
}
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,
OperationUnsupported,
NetworkDown,
FileDescriptorNotASocket,
SocketNotBound,
NoDevice,
} || UnexpectedError;
/// Set a socket's options.
pub fn setsockopt(fd: socket_t, level: i32, optname: u32, opt: []const u8) SetSockOptError!void {
if (native_os == .windows) {
const rc = windows.ws2_32.setsockopt(fd, level, @intCast(optname), opt.ptr, @intCast(opt.len));
if (rc == windows.ws2_32.SOCKET_ERROR) {
switch (windows.ws2_32.WSAGetLastError()) {
.NOTINITIALISED => unreachable,
.ENETDOWN => return error.NetworkDown,
.EFAULT => unreachable,
.ENOTSOCK => return error.FileDescriptorNotASocket,
.EINVAL => return error.SocketNotBound,
else => |err| return windows.unexpectedWSAError(err),
}
}
return;
} else {
switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(opt.len)))) {
.SUCCESS => {},
.BADF => unreachable, // always a race condition
.NOTSOCK => unreachable, // always a race condition
.INVAL => unreachable,
.FAULT => unreachable,
.DOM => return error.TimeoutTooBig,
.ISCONN => return error.AlreadyConnected,
.NOPROTOOPT => return error.InvalidProtocolOption,
.NOMEM => return error.SystemResources,
.NOBUFS => return error.SystemResources,
.PERM => return error.PermissionDenied,
.NODEV => return error.NoDevice,
.OPNOTSUPP => return error.OperationUnsupported,
else => |err| return unexpectedErrno(err),
}
}
}
pub const MemFdCreateError = error{
SystemFdQuotaExceeded,
ProcessFdQuotaExceeded,
OutOfMemory,
/// Either the name provided exceeded `NAME_MAX`, or invalid flags were passed.
NameTooLong,
SystemOutdated,
} || UnexpectedError;
pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t {
switch (native_os) {
.linux => {
// memfd_create is available only in glibc versions starting with 2.27 and bionic versions starting with 30.
const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ .major = 30, .minor = 0, .patch = 0 } else .{ .major = 2, .minor = 27, .patch = 0 });
const sys = if (use_c) std.c else linux;
const rc = sys.memfd_create(name, flags);
switch (sys.errno(rc)) {
.SUCCESS => return @intCast(rc),
.FAULT => unreachable, // name has invalid memory
.INVAL => return error.NameTooLong, // or, program has a bug and flags are faulty
.NFILE => return error.SystemFdQuotaExceeded,
.MFILE => return error.ProcessFdQuotaExceeded,
.NOMEM => return error.OutOfMemory,
else => |err| return unexpectedErrno(err),
}
},
.freebsd => {
if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
@compileError("memfd_create is unavailable on FreeBSD < 13.0");
const rc = system.memfd_create(name, flags);
switch (errno(rc)) {
.SUCCESS => return rc,
.BADF => unreachable, // name argument NULL
.INVAL => unreachable, // name too long or invalid/unsupported flags.
.MFILE => return error.ProcessFdQuotaExceeded,
.NFILE => return error.SystemFdQuotaExceeded,
.NOSYS => return error.SystemOutdated,
else => |err| return unexpectedErrno(err),
}
},
else => @compileError("target OS does not support memfd_create()"),
}
}
pub fn memfd_create(name: []const u8, flags: u32) MemFdCreateError!fd_t {
var buffer: [NAME_MAX - "memfd:".len - 1:0]u8 = undefined;
if (name.len > buffer.len) return error.NameTooLong;
@memcpy(buffer[0..name.len], name);
buffer[name.len] = 0;
return memfd_createZ(&buffer, flags);
}
pub fn getrusage(who: i32) rusage {
var result: rusage = undefined;
const rc = system.getrusage(who, &result);
switch (errno(rc)) {
.SUCCESS => return result,
.INVAL => unreachable,
.FAULT => unreachable,
else => unreachable,
}
}
pub const TIOCError = error{NotATerminal};
pub const TermiosGetError = TIOCError || UnexpectedError;
pub fn tcgetattr(handle: fd_t) TermiosGetError!termios {
while (true) {
var term: termios = undefined;
switch (errno(system.tcgetattr(handle, &term))) {
.SUCCESS => return term,
.INTR => continue,
.BADF => unreachable,
.NOTTY => return error.NotATerminal,
else => |err| return unexpectedErrno(err),
}
}
}
pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned};
pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) TermiosSetError!void {
while (true) {
switch (errno(system.tcsetattr(handle, optional_action, &termios_p))) {
.SUCCESS => return,
.BADF => unreachable,
.INTR => continue,
.INVAL => unreachable,
.NOTTY => return error.NotATerminal,
.IO => return error.ProcessOrphaned,
else => |err| return unexpectedErrno(err),
}
}
}
pub const TermioGetPgrpError = TIOCError || UnexpectedError;
/// Returns the process group ID for the TTY associated with the given handle.
pub fn tcgetpgrp(handle: fd_t) TermioGetPgrpError!pid_t {
while (true) {
var pgrp: pid_t = undefined;
switch (errno(system.tcgetpgrp(handle, &pgrp))) {
.SUCCESS => return pgrp,
.BADF => unreachable,
.INVAL => unreachable,
.INTR => continue,
.NOTTY => return error.NotATerminal,
else => |err| return unexpectedErrno(err),
}
}
}
pub const TermioSetPgrpError = TermioGetPgrpError || error{NotAPgrpMember};
/// Sets the controlling process group ID for given TTY.
/// handle must be valid fd_t to a TTY associated with calling process.
/// pgrp must be a valid process group, and the calling process must be a member
/// of that group.
pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void {
while (true) {
switch (errno(system.tcsetpgrp(handle, &pgrp))) {
.SUCCESS => return,
.BADF => unreachable,
.INVAL => unreachable,
.INTR => continue,
.NOTTY => return error.NotATerminal,
.PERM => return TermioSetPgrpError.NotAPgrpMember,
else => |err| return unexpectedErrno(err),
}
}
}
pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
const rc = system.signalfd(fd, mask, flags);
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.BADF, .INVAL => unreachable,
.NFILE => return error.SystemFdQuotaExceeded,
.NOMEM => return error.SystemResources,
.MFILE => return error.ProcessResources,
.NODEV => return error.InodeMountFail,
else => |err| return unexpectedErrno(err),
}
}
pub const SyncError = std.Io.File.SyncError;
/// Write all pending file contents and metadata modifications to all filesystems.
pub fn sync() void {
system.sync();
}
/// Write all pending file contents and metadata modifications to the filesystem which contains the specified file.
pub fn syncfs(fd: fd_t) SyncError!void {
const rc = system.syncfs(fd);
switch (errno(rc)) {
.SUCCESS => return,
.BADF, .INVAL, .ROFS => unreachable,
.IO => return error.InputOutput,
.NOSPC => return error.NoSpaceLeft,
.DQUOT => return error.DiskQuota,
else => |err| return unexpectedErrno(err),
}
}
/// Write all pending file contents for the specified file descriptor to the underlying filesystem, but not necessarily the metadata.
pub fn fdatasync(fd: fd_t) SyncError!void {
const rc = system.fdatasync(fd);
switch (errno(rc)) {
.SUCCESS => return,
.BADF, .INVAL, .ROFS => unreachable,
.IO => return error.InputOutput,
.NOSPC => return error.NoSpaceLeft,
.DQUOT => return error.DiskQuota,
else => |err| return unexpectedErrno(err),
}
}
pub const PrctlError = error{
/// Can only occur with PR_SET_SECCOMP/SECCOMP_MODE_FILTER or
/// PR_SET_MM/PR_SET_MM_EXE_FILE
AccessDenied,
/// Can only occur with PR_SET_MM/PR_SET_MM_EXE_FILE
InvalidFileDescriptor,
InvalidAddress,
/// Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT,
/// or PR_MPX_DISABLE_MANAGEMENT
UnsupportedFeature,
/// Can only occur with PR_SET_FP_MODE
OperationUnsupported,
PermissionDenied,
} || UnexpectedError;
pub fn prctl(option: PR, args: anytype) PrctlError!u31 {
if (@typeInfo(@TypeOf(args)) != .@"struct")
@compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
if (args.len > 4)
@compileError("prctl takes a maximum of 4 optional arguments");
var buf: [4]usize = undefined;
{
comptime var i = 0;
inline while (i < args.len) : (i += 1) buf[i] = args[i];
}
const rc = system.prctl(@intFromEnum(option), buf[0], buf[1], buf[2], buf[3]);
switch (errno(rc)) {
.SUCCESS => return @intCast(rc),
.ACCES => return error.AccessDenied,
.BADF => return error.InvalidFileDescriptor,
.FAULT => return error.InvalidAddress,
.INVAL => unreachable,
.NODEV, .NXIO => return error.UnsupportedFeature,
.OPNOTSUPP => return error.OperationUnsupported,
.PERM, .BUSY => return error.PermissionDenied,
.RANGE => unreachable,
else => |err| return unexpectedErrno(err),
}
}
pub const GetrlimitError = UnexpectedError;
pub fn getrlimit(resource: rlimit_resource) GetrlimitError!rlimit {
const getrlimit_sym = if (lfs64_abi) system.getrlimit64 else system.getrlimit;
var limits: rlimit = undefined;
switch (errno(getrlimit_sym(resource, &limits))) {
.SUCCESS => return limits,
.FAULT => unreachable, // bogus pointer
.INVAL => unreachable,
else => |err| return unexpectedErrno(err),
}
}
pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError;
pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void {
const setrlimit_sym = if (lfs64_abi) system.setrlimit64 else system.setrlimit;
switch (errno(setrlimit_sym(resource, &limits))) {
.SUCCESS => return,
.FAULT => unreachable, // bogus pointer
.INVAL => return error.LimitTooBig, // this could also mean "invalid resource", but that would be unreachable
.PERM => return error.PermissionDenied,
else => |err| return unexpectedErrno(err),
}
}
pub const MincoreError = error{
/// A kernel resource was temporarily unavailable.
SystemResources,
/// vec points to an invalid address.
InvalidAddress,
/// addr is not page-aligned.
InvalidSyscall,
/// One of the following:
/// * length is greater than user space TASK_SIZE - addr
/// * addr + length contains unmapped memory
OutOfMemory,
/// The mincore syscall is not available on this version and configuration
/// of this UNIX-like kernel.
MincoreUnavailable,
} || UnexpectedError;
/// Determine whether pages are resident in memory.
pub fn mincore(ptr: [*]align(page_size_min) u8, length: usize, vec: [*]u8) MincoreError!void {
return switch (errno(system.mincore(ptr, length, vec))) {
.SUCCESS => {},
.AGAIN => error.SystemResources,
.FAULT => error.InvalidAddress,
.INVAL => error.InvalidSyscall,
.NOMEM => error.OutOfMemory,
.NOSYS => error.MincoreUnavailable,
else => |err| unexpectedErrno(err),
};
}
pub const MadviseError = error{
/// advice is MADV.REMOVE, but the specified address range is not a shared writable mapping.
AccessDenied,
/// advice is MADV.HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability.
PermissionDenied,
/// A kernel resource was temporarily unavailable.
SystemResources,
/// One of the following:
/// * addr is not page-aligned or length is negative
/// * advice is not valid
/// * advice is MADV.DONTNEED or MADV.REMOVE and the specified address range
/// includes locked, Huge TLB pages, or VM_PFNMAP pages.
/// * advice is MADV.MERGEABLE or MADV.UNMERGEABLE, but the kernel was not
/// configured with CONFIG_KSM.
/// * advice is MADV.FREE or MADV.WIPEONFORK but the specified address range
/// includes file, Huge TLB, MAP.SHARED, or VM_PFNMAP ranges.
InvalidSyscall,
/// (for MADV.WILLNEED) Paging in this area would exceed the process's
/// maximum resident set size.
WouldExceedMaximumResidentSetSize,
/// One of the following:
/// * (for MADV.WILLNEED) Not enough memory: paging in failed.
/// * Addresses in the specified range are not currently mapped, or
/// are outside the address space of the process.
OutOfMemory,
/// The madvise syscall is not available on this version and configuration
/// of the Linux kernel.
MadviseUnavailable,
/// The operating system returned an undocumented error code.
Unexpected,
};
/// Give advice about use of memory.
/// This syscall is optional and is sometimes configured to be disabled.
pub fn madvise(ptr: [*]align(page_size_min) u8, length: usize, advice: u32) MadviseError!void {
switch (errno(system.madvise(ptr, length, advice))) {
.SUCCESS => return,
.PERM => return error.PermissionDenied,
.ACCES => return error.AccessDenied,
.AGAIN => return error.SystemResources,
.BADF => unreachable, // The map exists, but the area maps something that isn't a file.
.INVAL => return error.InvalidSyscall,
.IO => return error.WouldExceedMaximumResidentSetSize,
.NOMEM => return error.OutOfMemory,
.NOSYS => return error.MadviseUnavailable,
else => |err| return unexpectedErrno(err),
}
}
pub const PerfEventOpenError = error{
/// Returned if the perf_event_attr size value is too small (smaller
/// than PERF_ATTR_SIZE_VER0), too big (larger than the page size),
/// or larger than the kernel supports and the extra bytes are not
/// zero. When E2BIG is returned, the perf_event_attr size field is
/// overwritten by the kernel to be the size of the structure it was
/// expecting.
TooBig,
/// Returned when the requested event requires CAP_SYS_ADMIN permis
/// sions (or a more permissive perf_event paranoid setting). Some
/// common cases where an unprivileged process may encounter this
/// error: attaching to a process owned by a different user; moni
/// toring all processes on a given CPU (i.e., specifying the pid
/// argument as -1); and not setting exclude_kernel when the para
/// noid setting requires it.
/// Also:
/// Returned on many (but not all) architectures when an unsupported
/// exclude_hv, exclude_idle, exclude_user, or exclude_kernel set
/// ting is specified.
/// It can also happen, as with EACCES, when the requested event re
/// quires CAP_SYS_ADMIN permissions (or a more permissive
/// perf_event paranoid setting). This includes setting a break
/// point on a kernel address, and (since Linux 3.13) setting a ker
/// nel function-trace tracepoint.
PermissionDenied,
/// Returned if another event already has exclusive access to the
/// PMU.
DeviceBusy,
/// Each opened event uses one file descriptor. If a large number
/// of events are opened, the per-process limit on the number of
/// open file descriptors will be reached, and no more events can be
/// created.
ProcessResources,
EventRequiresUnsupportedCpuFeature,
/// Returned if you try to add more breakpoint
/// events than supported by the hardware.
TooManyBreakpoints,
/// Returned if PERF_SAMPLE_STACK_USER is set in sample_type and it
/// is not supported by hardware.
SampleStackNotSupported,
/// Returned if an event requiring a specific hardware feature is
/// requested but there is no hardware support. This includes re
/// questing low-skid events if not supported, branch tracing if it
/// is not available, sampling if no PMU interrupt is available, and
/// branch stacks for software events.
EventNotSupported,
/// Returned if PERF_SAMPLE_CALLCHAIN is requested and sam
/// ple_max_stack is larger than the maximum specified in
/// /proc/sys/kernel/perf_event_max_stack.
SampleMaxStackOverflow,
/// Returned if attempting to attach to a process that does not exist.
ProcessNotFound,
} || UnexpectedError;
pub fn perf_event_open(
attr: *system.perf_event_attr,
pid: pid_t,
cpu: i32,
group_fd: fd_t,
flags: usize,
) PerfEventOpenError!fd_t {
if (native_os == .linux) {
// There is no syscall wrapper for this function exposed by libcs
const rc = linux.perf_event_open(attr, pid, cpu, group_fd, flags);
switch (linux.errno(rc)) {
.SUCCESS => return @intCast(rc),
.@"2BIG" => return error.TooBig,
.ACCES => return error.PermissionDenied,
.BADF => unreachable, // group_fd file descriptor is not valid.
.BUSY => return error.DeviceBusy,
.FAULT => unreachable, // Segmentation fault.
.INVAL => unreachable, // Bad attr settings.
.INTR => unreachable, // Mixed perf and ftrace handling for a uprobe.
.MFILE => return error.ProcessResources,
.NODEV => return error.EventRequiresUnsupportedCpuFeature,
.NOENT => unreachable, // Invalid type setting.
.NOSPC => return error.TooManyBreakpoints,
.NOSYS => return error.SampleStackNotSupported,
.OPNOTSUPP => return error.EventNotSupported,
.OVERFLOW => return error.SampleMaxStackOverflow,
.PERM => return error.PermissionDenied,
.SRCH => return error.ProcessNotFound,
else => |err| return unexpectedErrno(err),
}
}
}
pub const TimerFdCreateError = error{
PermissionDenied,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
NoDevice,
SystemResources,
} || UnexpectedError;
pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError;
pub const TimerFdSetError = TimerFdGetError || error{Canceled};
pub fn timerfd_create(clock_id: system.timerfd_clockid_t, flags: system.TFD) TimerFdCreateError!fd_t {
const rc = system.timerfd_create(clock_id, @bitCast(flags));
return switch (errno(rc)) {
.SUCCESS => @intCast(rc),
.INVAL => unreachable,
.MFILE => return error.ProcessFdQuotaExceeded,
.NFILE => return error.SystemFdQuotaExceeded,
.NODEV => return error.NoDevice,
.NOMEM => return error.SystemResources,
.PERM => return error.PermissionDenied,
else => |err| return unexpectedErrno(err),
};
}
pub fn timerfd_settime(
fd: i32,
flags: system.TFD.TIMER,
new_value: *const system.itimerspec,
old_value: ?*system.itimerspec,
) TimerFdSetError!void {
const rc = system.timerfd_settime(fd, @bitCast(flags), new_value, old_value);
return switch (errno(rc)) {
.SUCCESS => {},
.BADF => error.InvalidHandle,
.FAULT => unreachable,
.INVAL => unreachable,
.CANCELED => error.Canceled,
else => |err| return unexpectedErrno(err),
};
}
pub fn timerfd_gettime(fd: i32) TimerFdGetError!system.itimerspec {
var curr_value: system.itimerspec = undefined;
const rc = system.timerfd_gettime(fd, &curr_value);
return switch (errno(rc)) {
.SUCCESS => return curr_value,
.BADF => error.InvalidHandle,
.FAULT => unreachable,
.INVAL => unreachable,
else => |err| return unexpectedErrno(err),
};
}
pub const PtraceError = error{
DeadLock,
DeviceBusy,
InputOutput,
NameTooLong,
OperationUnsupported,
OutOfMemory,
ProcessNotFound,
PermissionDenied,
} || UnexpectedError;
pub fn ptrace(request: u32, pid: pid_t, addr: usize, data: usize) PtraceError!void {
return switch (native_os) {
.windows,
.wasi,
.emscripten,
.haiku,
.illumos,
.plan9,
=> @compileError("ptrace unsupported by target OS"),
.linux => switch (errno(if (builtin.link_libc) std.c.ptrace(
@intCast(request),
pid,
@ptrFromInt(addr),
@ptrFromInt(data),
) else linux.ptrace(request, pid, addr, data, 0))) {
.SUCCESS => {},
.SRCH => error.ProcessNotFound,
.FAULT => unreachable,
.INVAL => unreachable,
.IO => return error.InputOutput,
.PERM => error.PermissionDenied,
.BUSY => error.DeviceBusy,
else => |err| return unexpectedErrno(err),
},
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (errno(std.c.ptrace(
@enumFromInt(request),
pid,
@ptrFromInt(addr),
@intCast(data),
))) {
.SUCCESS => {},
.SRCH => error.ProcessNotFound,
.INVAL => unreachable,
.PERM => error.PermissionDenied,
.BUSY => error.DeviceBusy,
else => |err| return unexpectedErrno(err),
},
.dragonfly => switch (errno(std.c.ptrace(
@intCast(request),
pid,
@ptrFromInt(addr),
@intCast(data),
))) {
.SUCCESS => {},
.SRCH => error.ProcessNotFound,
.INVAL => unreachable,
.PERM => error.PermissionDenied,
.BUSY => error.DeviceBusy,
else => |err| return unexpectedErrno(err),
},
.freebsd => switch (errno(std.c.ptrace(
@intCast(request),
pid,
@ptrFromInt(addr),
@intCast(data),
))) {
.SUCCESS => {},
.SRCH => error.ProcessNotFound,
.INVAL => unreachable,
.PERM => error.PermissionDenied,
.BUSY => error.DeviceBusy,
.NOENT, .NOMEM => error.OutOfMemory,
.NAMETOOLONG => error.NameTooLong,
else => |err| return unexpectedErrno(err),
},
.netbsd => switch (errno(std.c.ptrace(
@intCast(request),
pid,
@ptrFromInt(addr),
@intCast(data),
))) {
.SUCCESS => {},
.SRCH => error.ProcessNotFound,
.INVAL => unreachable,
.PERM => error.PermissionDenied,
.BUSY => error.DeviceBusy,
.DEADLK => error.DeadLock,
else => |err| return unexpectedErrno(err),
},
.openbsd => switch (errno(std.c.ptrace(
@intCast(request),
pid,
@ptrFromInt(addr),
@intCast(data),
))) {
.SUCCESS => {},
.SRCH => error.ProcessNotFound,
.INVAL => unreachable,
.PERM => error.PermissionDenied,
.BUSY => error.DeviceBusy,
.NOTSUP => error.OperationUnsupported,
else => |err| return unexpectedErrno(err),
},
else => @compileError("std.posix.ptrace unimplemented for target OS"),
};
}
pub const NameToFileHandleAtError = error{
FileNotFound,
NotDir,
OperationUnsupported,
NameTooLong,
Unexpected,
};
pub fn name_to_handle_at(
dirfd: fd_t,
pathname: []const u8,
handle: *std.os.linux.file_handle,
mount_id: *i32,
flags: u32,
) NameToFileHandleAtError!void {
const pathname_c = try toPosixPath(pathname);
return name_to_handle_atZ(dirfd, &pathname_c, handle, mount_id, flags);
}
pub fn name_to_handle_atZ(
dirfd: fd_t,
pathname_z: [*:0]const u8,
handle: *std.os.linux.file_handle,
mount_id: *i32,
flags: u32,
) NameToFileHandleAtError!void {
switch (errno(system.name_to_handle_at(dirfd, pathname_z, handle, mount_id, flags))) {
.SUCCESS => {},
.FAULT => unreachable, // pathname, mount_id, or handle outside accessible address space
.INVAL => unreachable, // bad flags, or handle_bytes too big
.NOENT => return error.FileNotFound,
.NOTDIR => return error.NotDir,
.OPNOTSUPP => return error.OperationUnsupported,
.OVERFLOW => return error.NameTooLong,
else => |err| return unexpectedErrno(err),
}
}
pub const IoCtl_SIOCGIFINDEX_Error = error{
FileSystem,
InterfaceNotFound,
} || UnexpectedError;
pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
while (true) {
switch (errno(system.ioctl(fd, SIOCGIFINDEX, @intFromPtr(ifr)))) {
.SUCCESS => return,
.INVAL => unreachable, // Bad parameters.
.NOTTY => unreachable,
.NXIO => unreachable,
.BADF => unreachable, // Always a race condition.
.FAULT => unreachable, // Bad pointer parameter.
.INTR => continue,
.IO => return error.FileSystem,
.NODEV => return error.InterfaceNotFound,
else => |err| return unexpectedErrno(err),
}
}
}
pub const lfs64_abi = native_os == .linux and builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid());
/// Whether or not `error.Unexpected` will print its value and a stack trace.
///
/// If this happens the fix is to add the error code to the corresponding
/// switch expression, possibly introduce a new error in the error set, and
/// send a patch to Zig.
pub const unexpected_error_tracing = builtin.mode == .Debug and switch (builtin.zig_backend) {
.stage2_llvm, .stage2_x86_64 => true,
else => false,
};
pub const UnexpectedError = std.Io.UnexpectedError;
/// Call this when you made a syscall or something that sets errno
/// and you get an unexpected error.
pub fn unexpectedErrno(err: E) UnexpectedError {
if (unexpected_error_tracing) {
std.debug.print("unexpected errno: {d}\n", .{@intFromEnum(err)});
std.debug.dumpCurrentStackTrace(.{});
}
return error.Unexpected;
}
/// Used to convert a slice to a null terminated slice on the stack.
pub fn toPosixPath(file_path: []const u8) error{NameTooLong}![PATH_MAX - 1:0]u8 {
if (std.debug.runtime_safety) assert(mem.findScalar(u8, file_path, 0) == null);
var path_with_null: [PATH_MAX - 1:0]u8 = undefined;
// >= rather than > to make room for the null byte
if (file_path.len >= PATH_MAX) return error.NameTooLong;
@memcpy(path_with_null[0..file_path.len], file_path);
path_with_null[file_path.len] = 0;
return path_with_null;
}