1//! POSIX API layer.
2//!
3//! This is more cross platform than using OS-specific APIs, however, it is
4//! lower-level and less portable than other namespaces such as `std.Io` and
5//! `std.process`.
6//!
7//! These APIs are generally lowered to libc function calls if and only if libc
8//! is linked. Most operating systems other than Windows, Linux, and WASI
9//! require always linking libc because they use it as the stable syscall ABI.
10const builtin = @import("builtin");
11const native_os = builtin.os.tag;
12
13const std = @import("std.zig");
14const Io = std.Io;
15const mem = std.mem;
16const maxInt = std.math.maxInt;
17const cast = std.math.cast;
18const assert = std.debug.assert;
19const page_size_min = std.heap.page_size_min;
20
21test {
22 _ = @import("posix/test.zig");
23}
24
25/// Whether to use libc for the POSIX API layer.
26const use_libc = builtin.link_libc or switch (native_os) {
27 .windows, .wasi => true,
28 else => false,
29};
30
31const linux = std.os.linux;
32const windows = std.os.windows;
33const wasi = std.os.wasi;
34
35/// A libc-compatible API layer.
36pub const system = if (use_libc)
37 std.c
38else switch (native_os) {
39 .linux => linux,
40 .plan9 => std.os.plan9,
41 .psp => struct {
42 pub const fd_t = i32;
43 pub const pid_t = void;
44 pub const pollfd = void;
45 pub const uid_t = void;
46 pub const gid_t = void;
47 pub const mode_t = u32;
48 pub const nlink_t = u32;
49 pub const blksize_t = u32;
50 pub const ino_t = u64;
51 pub const IFNAMESIZE = {};
52 pub const SIG = void;
53
54 // https://github.com/pspdev/newlib/blob/9e0a073634ad73e8e088f2e071c55a9fe5d39709/newlib/libc/sys/psp/sys/dirent.h#L19
55 pub const NAME_MAX = 255;
56 },
57 else => struct {
58 pub const pid_t = void;
59 pub const pollfd = void;
60 pub const fd_t = void;
61 pub const uid_t = void;
62 pub const gid_t = void;
63 pub const mode_t = u0;
64 pub const nlink_t = u0;
65 pub const blksize_t = void;
66 pub const ino_t = void;
67 pub const IFNAMESIZE = {};
68 pub const SIG = void;
69 },
70};
71
72pub const AF = system.AF;
73pub const AF_SUN = system.AF_SUN;
74pub const AI = system.AI;
75pub const ARCH = system.ARCH;
76pub const AT = system.AT;
77pub const AT_SUN = system.AT_SUN;
78pub const CLOCK = system.CLOCK;
79pub const CPU_COUNT = system.CPU_COUNT;
80pub const CTL = system.CTL;
81pub const DT = system.DT;
82pub const E = system.E;
83pub const Elf_Symndx = system.Elf_Symndx;
84pub const F = system.F;
85pub const FD_CLOEXEC = system.FD_CLOEXEC;
86pub const Flock = system.Flock;
87pub const HOST_NAME_MAX = system.HOST_NAME_MAX;
88pub const HW = system.HW;
89pub const IFNAMESIZE = system.IFNAMESIZE;
90pub const IOV_MAX = system.IOV_MAX;
91pub const IP = system.IP;
92pub const IPV6 = system.IPV6;
93pub const IPPROTO = system.IPPROTO;
94pub const IPTOS = system.IPTOS;
95pub const KERN = system.KERN;
96pub const Kevent = system.Kevent;
97pub const MADV = system.MADV;
98pub const MAP = system.MAP;
99pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;
100pub const MCL = system.MCL;
101pub const MFD = system.MFD;
102pub const MLOCK = system.MLOCK;
103pub const MREMAP = system.MREMAP;
104pub const MSF = system.MSF;
105pub const MSG = system.MSG;
106pub const NAME_MAX = system.NAME_MAX;
107pub const NSIG = system.NSIG;
108pub const O = system.O;
109pub const PATH_MAX = system.PATH_MAX;
110pub const POLL = system.POLL;
111pub const POSIX_FADV = system.POSIX_FADV;
112pub const PR = system.PR;
113pub const PROT = system.PROT;
114pub const RLIM = system.RLIM;
115pub const S = system.S;
116pub const SA = system.SA;
117pub const SC = system.SC;
118pub const SCM = system.SCM;
119pub const SEEK = system.SEEK;
120pub const SHUT = system.SHUT;
121pub const SIG = system.SIG;
122pub const SIOCGIFINDEX = system.SIOCGIFINDEX;
123pub const SO = system.SO;
124pub const SOCK = system.SOCK;
125pub const SOL = system.SOL;
126pub const IFF = system.IFF;
127pub const STDERR_FILENO = system.STDERR_FILENO;
128pub const STDIN_FILENO = system.STDIN_FILENO;
129pub const STDOUT_FILENO = system.STDOUT_FILENO;
130pub const SYS = system.SYS;
131pub const Sigaction = system.Sigaction;
132/// Windows has no concept of `stat`.
133///
134/// On Linux, the `stat` bits/wrappers are removed due to having to maintain
135/// the different varying stat structs per target and libc, leading to runtime
136/// errors. Users targeting Linux should add a comptime check and use statx,
137/// similar to how `Io.File.stat` does.
138pub const Stat = switch (native_os) {
139 .windows => void,
140 .linux => void,
141 else => system.Stat,
142};
143pub const T = system.T;
144pub const TCP = system.TCP;
145pub const VDSO = system.VDSO;
146pub const W = system.W;
147pub const _SC = system._SC;
148pub const addrinfo = system.addrinfo;
149pub const blkcnt_t = system.blkcnt_t;
150pub const blksize_t = system.blksize_t;
151pub const clock_t = system.clock_t;
152pub const clockid_t = system.clockid_t;
153pub const timerfd_clockid_t = system.timerfd_clockid_t;
154pub const cpu_set_t = system.cpu_set_t;
155pub const dev_t = system.dev_t;
156pub const dl_phdr_info = system.dl_phdr_info;
157pub const fd_t = system.fd_t;
158pub const file_obj = system.file_obj;
159pub const gid_t = system.gid_t;
160pub const ifreq = system.ifreq;
161pub const in_pktinfo = system.in_pktinfo;
162pub const in6_pktinfo = system.in6_pktinfo;
163pub const ino_t = system.ino_t;
164pub const linger = system.linger;
165pub const mode_t = system.mode_t;
166pub const msghdr = system.msghdr;
167pub const msghdr_const = system.msghdr_const;
168pub const nfds_t = system.nfds_t;
169pub const nlink_t = system.nlink_t;
170pub const off_t = system.off_t;
171pub const pid_t = system.pid_t;
172pub const pollfd = system.pollfd;
173pub const port_event = system.port_event;
174pub const port_notify = system.port_notify;
175pub const port_t = system.port_t;
176pub const rlim_t = system.rlim_t;
177pub const rlimit = system.rlimit;
178pub const rlimit_resource = system.rlimit_resource;
179pub const rusage = system.rusage;
180pub const sa_family_t = system.sa_family_t;
181pub const siginfo_t = system.siginfo_t;
182pub const sigset_t = system.sigset_t;
183pub const sigrtmin = system.sigrtmin;
184pub const sigrtmax = system.sigrtmax;
185pub const sockaddr = system.sockaddr;
186pub const socklen_t = system.socklen_t;
187pub const stack_t = system.stack_t;
188pub const time_t = system.time_t;
189pub const timespec = system.timespec;
190pub const timestamp_t = system.timestamp_t;
191pub const timeval = system.timeval;
192pub const timezone = system.timezone;
193pub const UTIME = system.UTIME;
194pub const uid_t = system.uid_t;
195pub const user_desc = system.user_desc;
196pub const utsname = system.utsname;
197
198pub const termios = system.termios;
199pub const CSIZE = system.CSIZE;
200pub const NCCS = system.NCCS;
201pub const cc_t = system.cc_t;
202pub const V = system.V;
203pub const speed_t = system.speed_t;
204pub const tc_iflag_t = system.tc_iflag_t;
205pub const tc_oflag_t = system.tc_oflag_t;
206pub const tc_cflag_t = system.tc_cflag_t;
207pub const tc_lflag_t = system.tc_lflag_t;
208
209pub const F_OK = system.F_OK;
210pub const R_OK = system.R_OK;
211pub const W_OK = system.W_OK;
212pub const X_OK = system.X_OK;
213
214pub const iovec = extern struct {
215 base: [*]u8,
216 len: usize,
217};
218
219pub const iovec_const = extern struct {
220 base: [*]const u8,
221 len: usize,
222};
223
224pub const ACCMODE = switch (native_os) {
225 // POSIX has a note about the access mode values:
226 //
227 // In historical implementations the value of O_RDONLY is zero. Because of
228 // that, it is not possible to detect the presence of O_RDONLY and another
229 // option. Future implementations should encode O_RDONLY and O_WRONLY as
230 // bit flags so that: O_RDONLY | O_WRONLY == O_RDWR
231 //
232 // In practice SerenityOS is the only system supported by Zig that
233 // implements this suggestion.
234 // https://github.com/SerenityOS/serenity/blob/4adc51fdf6af7d50679c48b39362e062f5a3b2cb/Kernel/API/POSIX/fcntl.h#L28-L30
235 .serenity => enum(u2) {
236 NONE = 0,
237 RDONLY = 1,
238 WRONLY = 2,
239 RDWR = 3,
240 },
241 else => enum(u2) {
242 RDONLY = 0,
243 WRONLY = 1,
244 RDWR = 2,
245 },
246};
247
248pub const TCSA = enum(c_uint) {
249 NOW,
250 DRAIN,
251 FLUSH,
252 _,
253};
254
255pub const winsize = extern struct {
256 row: u16,
257 col: u16,
258 xpixel: u16,
259 ypixel: u16,
260};
261
262pub const LOCK = struct {
263 pub const SH = 1;
264 pub const EX = 2;
265 pub const NB = 4;
266 pub const UN = 8;
267};
268
269pub const LOG = struct {
270 /// system is unusable
271 pub const EMERG = 0;
272 /// action must be taken immediately
273 pub const ALERT = 1;
274 /// critical conditions
275 pub const CRIT = 2;
276 /// error conditions
277 pub const ERR = 3;
278 /// warning conditions
279 pub const WARNING = 4;
280 /// normal but significant condition
281 pub const NOTICE = 5;
282 /// informational
283 pub const INFO = 6;
284 /// debug-level messages
285 pub const DEBUG = 7;
286};
287
288pub const socket_t = fd_t;
289
290/// Obtains errno from the return value of a system function call.
291///
292/// For some systems this will obtain the value directly from the syscall return value;
293/// for others it will use a thread-local errno variable. Therefore, this
294/// function only returns a well-defined value when it is called directly after
295/// the system function call whose errno value is intended to be observed.
296pub const errno = system.errno;
297
298pub const RebootError = error{
299 PermissionDenied,
300} || UnexpectedError;
301
302pub const RebootCommand = switch (native_os) {
303 .linux => union(linux.LINUX_REBOOT.CMD) {
304 RESTART: void,
305 HALT: void,
306 CAD_ON: void,
307 CAD_OFF: void,
308 POWER_OFF: void,
309 RESTART2: [*:0]const u8,
310 SW_SUSPEND: void,
311 KEXEC: void,
312 },
313 else => @compileError("Unsupported OS"),
314};
315
316pub fn reboot(cmd: RebootCommand) RebootError!void {
317 switch (native_os) {
318 .linux => {
319 switch (linux.errno(linux.reboot(
320 .MAGIC1,
321 .MAGIC2,
322 cmd,
323 switch (cmd) {
324 .RESTART2 => |s| s,
325 else => null,
326 },
327 ))) {
328 .SUCCESS => {},
329 .PERM => return error.PermissionDenied,
330 else => |err| return std.posix.unexpectedErrno(err),
331 }
332 switch (cmd) {
333 .CAD_OFF => {},
334 .CAD_ON => {},
335 .SW_SUSPEND => {},
336
337 .HALT => unreachable,
338 .KEXEC => unreachable,
339 .POWER_OFF => unreachable,
340 .RESTART => unreachable,
341 .RESTART2 => unreachable,
342 }
343 },
344 else => @compileError("Unsupported OS"),
345 }
346}
347
348pub const RaiseError = UnexpectedError;
349
350pub fn raise(sig: SIG) RaiseError!void {
351 if (builtin.link_libc) {
352 switch (errno(system.raise(sig))) {
353 .SUCCESS => return,
354 else => |err| return unexpectedErrno(err),
355 }
356 }
357
358 if (native_os == .linux) {
359 // Block all signals so a `fork` (from a signal handler) between the gettid() and kill() syscalls
360 // cannot trigger an extra, unexpected, inter-process signal. Signal paranoia inherited from Musl.
361 const filled = linux.sigfillset();
362 var orig: sigset_t = undefined;
363 sigprocmask(SIG.BLOCK, &filled, &orig);
364 const rc = linux.tkill(linux.gettid(), sig);
365 sigprocmask(SIG.SETMASK, &orig, null);
366
367 switch (errno(rc)) {
368 .SUCCESS => return,
369 else => |err| return unexpectedErrno(err),
370 }
371 }
372
373 @compileError("std.posix.raise unimplemented for this target");
374}
375
376pub const KillError = error{ ProcessNotFound, PermissionDenied } || UnexpectedError;
377
378pub fn kill(pid: pid_t, sig: SIG) KillError!void {
379 switch (errno(system.kill(pid, sig))) {
380 .SUCCESS => return,
381 .INVAL => unreachable, // invalid signal
382 .PERM => return error.PermissionDenied,
383 .SRCH => return error.ProcessNotFound,
384 else => |err| return unexpectedErrno(err),
385 }
386}
387
388pub const ReadError = std.Io.File.Reader.Error;
389
390/// Returns the number of bytes that were read, which can be less than
391/// buf.len. If 0 bytes were read, that means EOF.
392/// If `fd` is opened in non blocking mode, the function will return error.WouldBlock
393/// when EAGAIN is received.
394///
395/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
396/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
397/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
398/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
399/// The corresponding POSIX limit is `maxInt(isize)`.
400pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
401 if (buf.len == 0) return 0;
402 if (native_os == .windows) @compileError("unsupported OS");
403 if (native_os == .wasi) @compileError("unsupported OS");
404
405 // Prevents EINVAL.
406 const max_count = switch (native_os) {
407 .linux => 0x7ffff000,
408 .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => maxInt(i32),
409 else => maxInt(isize),
410 };
411 while (true) {
412 const rc = system.read(fd, buf.ptr, @min(buf.len, max_count));
413 switch (errno(rc)) {
414 .SUCCESS => return @intCast(rc),
415 .INTR => continue,
416 .INVAL => unreachable,
417 .FAULT => unreachable,
418 .AGAIN => return error.WouldBlock,
419 .CANCELED => return error.Canceled,
420 .BADF => return error.Unexpected, // use after free
421 .IO => return error.InputOutput,
422 .ISDIR => return error.IsDir,
423 .NOBUFS => return error.SystemResources,
424 .NOMEM => return error.SystemResources,
425 .NOTCONN => return error.SocketUnconnected,
426 .CONNRESET => return error.ConnectionResetByPeer,
427 .TIMEDOUT => return error.Unexpected,
428 else => |err| return unexpectedErrno(err),
429 }
430 }
431}
432
433pub const OpenError = std.Io.File.OpenError || error{WouldBlock};
434
435/// Open and possibly create a file. Keeps trying if it gets interrupted.
436/// `file_path` is relative to the open directory handle `dir_fd`.
437/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
438/// On WASI, `file_path` should be encoded as valid UTF-8.
439/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
440/// See also `openatZ`.
441pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: O, mode: mode_t) OpenError!fd_t {
442 if (native_os == .windows) {
443 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
444 } else if (native_os == .wasi and !builtin.link_libc) {
445 @compileError("use std.Io instead");
446 }
447 const file_path_c = try toPosixPath(file_path);
448 return openatZ(dir_fd, &file_path_c, flags, mode);
449}
450
451/// Open and possibly create a file. Keeps trying if it gets interrupted.
452/// `file_path` is relative to the open directory handle `dir_fd`.
453/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
454/// On WASI, `file_path` should be encoded as valid UTF-8.
455/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
456/// See also `openat`.
457pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) OpenError!fd_t {
458 if (native_os == .windows) {
459 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
460 } else if (native_os == .wasi and !builtin.link_libc) {
461 return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode);
462 }
463
464 const openat_sym = if (lfs64_abi) system.openat64 else system.openat;
465 while (true) {
466 const rc = openat_sym(dir_fd, file_path, flags, mode);
467 switch (errno(rc)) {
468 .SUCCESS => return @intCast(rc),
469 .INTR => continue,
470
471 .FAULT => unreachable,
472 .INVAL => return error.BadPathName,
473 .BADF => unreachable,
474 .ACCES => return error.AccessDenied,
475 .FBIG => return error.FileTooBig,
476 .OVERFLOW => return error.FileTooBig,
477 .ISDIR => return error.IsDir,
478 .LOOP => return error.SymLinkLoop,
479 .MFILE => return error.ProcessFdQuotaExceeded,
480 .NAMETOOLONG => return error.NameTooLong,
481 .NFILE => return error.SystemFdQuotaExceeded,
482 .NODEV => return error.NoDevice,
483 .NOENT => return error.FileNotFound,
484 .SRCH => return error.FileNotFound,
485 .NOMEM => return error.SystemResources,
486 .NOSPC => return error.NoSpaceLeft,
487 .NOTDIR => return error.NotDir,
488 .PERM => return error.PermissionDenied,
489 .EXIST => return error.PathAlreadyExists,
490 .BUSY => return error.DeviceBusy,
491 .OPNOTSUPP => return error.FileLocksUnsupported,
492 .AGAIN => return error.WouldBlock,
493 .TXTBSY => return error.FileBusy,
494 .NXIO => return error.NoDevice,
495 .ILSEQ => return error.BadPathName,
496 else => |err| return unexpectedErrno(err),
497 }
498 }
499}
500
501pub fn getppid() pid_t {
502 return system.getppid();
503}
504
505pub const GetSockNameError = error{
506 /// Insufficient resources were available in the system to perform the operation.
507 SystemResources,
508
509 /// The network subsystem has failed.
510 NetworkDown,
511
512 /// Socket hasn't been bound yet
513 SocketNotBound,
514
515 FileDescriptorNotASocket,
516
517 /// The socket is not connected (connection-oriented sockets only).
518 SocketUnconnected,
519} || UnexpectedError;
520
521pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
522 if (native_os == .windows) {
523 @compileError("use std.Io instead");
524 } else {
525 const rc = system.getpeername(sock, addr, addrlen);
526 switch (errno(rc)) {
527 .SUCCESS => return,
528 else => |err| return unexpectedErrno(err),
529
530 .BADF => unreachable, // always a race condition
531 .FAULT => unreachable,
532 .INVAL => unreachable, // invalid parameters
533 .NOTSOCK => return error.FileDescriptorNotASocket,
534 .NOBUFS => return error.SystemResources,
535 .NOTCONN => return error.SocketUnconnected,
536 }
537 }
538}
539
540pub const FanotifyInitError = error{
541 ProcessFdQuotaExceeded,
542 SystemFdQuotaExceeded,
543 SystemResources,
544 PermissionDenied,
545 /// The kernel does not recognize the flags passed, likely because it is an
546 /// older version.
547 UnsupportedFlags,
548} || UnexpectedError;
549
550pub fn fanotify_init(flags: std.os.linux.fanotify.InitFlags, event_f_flags: u32) FanotifyInitError!i32 {
551 const rc = system.fanotify_init(flags, event_f_flags);
552 switch (errno(rc)) {
553 .SUCCESS => return @intCast(rc),
554 .INVAL => return error.UnsupportedFlags,
555 .MFILE => return error.ProcessFdQuotaExceeded,
556 .NFILE => return error.SystemFdQuotaExceeded,
557 .NOMEM => return error.SystemResources,
558 .PERM => return error.PermissionDenied,
559 else => |err| return unexpectedErrno(err),
560 }
561}
562
563pub const FanotifyMarkError = error{
564 MarkAlreadyExists,
565 IsDir,
566 NotAssociatedWithFileSystem,
567 FileNotFound,
568 SystemResources,
569 UserMarkQuotaExceeded,
570 NotDir,
571 OperationUnsupported,
572 PermissionDenied,
573 CrossDevice,
574 NameTooLong,
575} || UnexpectedError;
576
577pub fn fanotify_mark(
578 fanotify_fd: fd_t,
579 flags: std.os.linux.fanotify.MarkFlags,
580 mask: std.os.linux.fanotify.MarkMask,
581 dirfd: fd_t,
582 pathname: ?[]const u8,
583) FanotifyMarkError!void {
584 if (pathname) |path| {
585 const path_c = try toPosixPath(path);
586 return fanotify_markZ(fanotify_fd, flags, mask, dirfd, &path_c);
587 } else {
588 return fanotify_markZ(fanotify_fd, flags, mask, dirfd, null);
589 }
590}
591
592pub fn fanotify_markZ(
593 fanotify_fd: fd_t,
594 flags: std.os.linux.fanotify.MarkFlags,
595 mask: std.os.linux.fanotify.MarkMask,
596 dirfd: fd_t,
597 pathname: ?[*:0]const u8,
598) FanotifyMarkError!void {
599 const rc = system.fanotify_mark(fanotify_fd, flags, mask, dirfd, pathname);
600 switch (errno(rc)) {
601 .SUCCESS => return,
602 .BADF => unreachable,
603 .EXIST => return error.MarkAlreadyExists,
604 .INVAL => unreachable,
605 .ISDIR => return error.IsDir,
606 .NODEV => return error.NotAssociatedWithFileSystem,
607 .NOENT => return error.FileNotFound,
608 .NOMEM => return error.SystemResources,
609 .NOSPC => return error.UserMarkQuotaExceeded,
610 .NOTDIR => return error.NotDir,
611 .OPNOTSUPP => return error.OperationUnsupported,
612 .PERM => return error.PermissionDenied,
613 .XDEV => return error.CrossDevice,
614 else => |err| return unexpectedErrno(err),
615 }
616}
617
618pub const MMapError = error{
619 /// The underlying filesystem of the specified file does not support memory mapping.
620 MemoryMappingNotSupported,
621 /// A file descriptor refers to a non-regular file. Or a file mapping was requested,
622 /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested
623 /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode.
624 /// Or `PROT_WRITE` is set, but the file is append-only.
625 AccessDenied,
626 /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
627 /// a filesystem that was mounted no-exec.
628 PermissionDenied,
629 LockedMemoryLimitExceeded,
630 ProcessFdQuotaExceeded,
631 SystemFdQuotaExceeded,
632 OutOfMemory,
633 /// Using FIXED_NOREPLACE flag and the process has already mapped memory at the given address
634 MappingAlreadyExists,
635} || UnexpectedError;
636
637/// Map files or devices into memory.
638/// `length` does not need to be aligned.
639/// Use of a mapped region can result in these signals:
640/// * SIGSEGV - Attempted write into a region mapped as read-only.
641/// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file
642pub fn mmap(
643 ptr: ?[*]align(page_size_min) u8,
644 length: usize,
645 prot: PROT,
646 flags: MAP,
647 fd: fd_t,
648 offset: u64,
649) MMapError![]align(page_size_min) u8 {
650 const mmap_sym = if (lfs64_abi) system.mmap64 else system.mmap;
651 const rc = mmap_sym(ptr, length, prot, @bitCast(flags), fd, @bitCast(offset));
652 const err: E = if (builtin.link_libc) blk: {
653 if (rc != std.c.MAP_FAILED) return @as([*]align(page_size_min) u8, @ptrCast(@alignCast(rc)))[0..length];
654 break :blk @fromBackingInt(@intCast(system._errno().*));
655 } else blk: {
656 const err = errno(rc);
657 if (err == .SUCCESS) return @as([*]align(page_size_min) u8, @ptrFromInt(rc))[0..length];
658 break :blk err;
659 };
660 switch (err) {
661 .SUCCESS => unreachable,
662 .TXTBSY => return error.AccessDenied,
663 .ACCES => return error.AccessDenied,
664 .PERM => return error.PermissionDenied,
665 .AGAIN => return error.LockedMemoryLimitExceeded,
666 .BADF => unreachable, // Always a race condition.
667 .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
668 .NODEV => return error.MemoryMappingNotSupported,
669 .INVAL => unreachable, // Invalid parameters to mmap()
670 .MFILE => return error.ProcessFdQuotaExceeded,
671 .NFILE => return error.SystemFdQuotaExceeded,
672 .NOMEM => return error.OutOfMemory,
673 .EXIST => return error.MappingAlreadyExists,
674 else => return unexpectedErrno(err),
675 }
676}
677
678/// Deletes the mappings for the specified address range, causing
679/// further references to addresses within the range to generate invalid memory references.
680/// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
681/// Zig's munmap function does not, for two reasons:
682/// * It violates the Zig principle that resource deallocation must succeed.
683/// * The Windows function, NtFreeVirtualMemory, has this restriction.
684pub fn munmap(memory: []align(page_size_min) const u8) void {
685 switch (errno(system.munmap(memory.ptr, memory.len))) {
686 .SUCCESS => return,
687 .INVAL => unreachable, // Invalid parameters.
688 .NOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
689 else => |e| if (std.options.unexpected_error_tracing) {
690 std.debug.panic("unexpected errno: {d} ({t})", .{ @backingInt(e), e });
691 } else unreachable,
692 }
693}
694
695pub const MRemapError = error{
696 LockedMemoryLimitExceeded,
697 /// Either a bug in the calling code, or the operating system abused the
698 /// EINVAL error code.
699 InvalidSyscallParameters,
700 OutOfMemory,
701} || UnexpectedError;
702
703pub fn mremap(
704 old_address: ?[*]align(page_size_min) u8,
705 old_len: usize,
706 new_len: usize,
707 flags: system.MREMAP,
708 new_address: ?[*]align(page_size_min) u8,
709) MRemapError![]align(page_size_min) u8 {
710 const rc = system.mremap(old_address, old_len, new_len, flags, new_address);
711 const err: E = if (builtin.link_libc) blk: {
712 if (rc != std.c.MAP_FAILED) return @as([*]align(page_size_min) u8, @ptrCast(@alignCast(rc)))[0..new_len];
713 break :blk @fromBackingInt(@intCast(system._errno().*));
714 } else blk: {
715 const err = errno(rc);
716 if (err == .SUCCESS) return @as([*]align(page_size_min) u8, @ptrFromInt(rc))[0..new_len];
717 break :blk err;
718 };
719 switch (err) {
720 .SUCCESS => unreachable,
721 .AGAIN => return error.LockedMemoryLimitExceeded,
722 .INVAL => return error.InvalidSyscallParameters,
723 .NOMEM => return error.OutOfMemory,
724 .FAULT => unreachable,
725 else => return unexpectedErrno(err),
726 }
727}
728
729pub const MSyncError = error{
730 UnmappedMemory,
731 PermissionDenied,
732} || UnexpectedError;
733
734pub fn msync(memory: []align(page_size_min) u8, flags: i32) MSyncError!void {
735 switch (errno(system.msync(memory.ptr, memory.len, flags))) {
736 .SUCCESS => return,
737 .PERM => return error.PermissionDenied,
738 .NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory
739 .INVAL => unreachable, // Invalid parameters.
740 else => unreachable,
741 }
742}
743
744pub const SysCtlError = error{
745 PermissionDenied,
746 SystemResources,
747 NameTooLong,
748 UnknownName,
749} || UnexpectedError;
750
751pub fn sysctl(
752 name: []const c_int,
753 oldp: ?*anyopaque,
754 oldlenp: ?*usize,
755 newp: ?*anyopaque,
756 newlen: usize,
757) SysCtlError!void {
758 if (native_os == .wasi) {
759 @compileError("sysctl not supported on WASI");
760 }
761 if (native_os == .haiku) {
762 @compileError("sysctl not supported on Haiku");
763 }
764
765 const name_len = cast(c_uint, name.len) orelse return error.NameTooLong;
766 switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {
767 .SUCCESS => return,
768 .FAULT => unreachable,
769 .PERM => return error.PermissionDenied,
770 .NOMEM => return error.SystemResources,
771 .NOENT => return error.UnknownName,
772 else => |err| return unexpectedErrno(err),
773 }
774}
775
776pub fn getSelfPhdrs() []std.elf.ElfN.Phdr {
777 const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval;
778 assert(getauxval(std.elf.AT_PHENT) == @sizeOf(std.elf.ElfN.Phdr));
779 const phdrs: [*]std.elf.ElfN.Phdr = @ptrFromInt(getauxval(std.elf.AT_PHDR));
780 return phdrs[0..getauxval(std.elf.AT_PHNUM)];
781}
782
783pub fn dl_iterate_phdr(
784 context: anytype,
785 comptime Error: type,
786 comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
787) Error!void {
788 const Context = @TypeOf(context);
789 const elf = std.elf;
790 const dl = @import("dynamic_library.zig");
791
792 switch (builtin.object_format) {
793 .elf, .c => {},
794 else => @compileError("dl_iterate_phdr is not available for this target"),
795 }
796
797 if (builtin.link_libc) {
798 switch (system.dl_iterate_phdr(struct {
799 fn callbackC(info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.c) c_int {
800 const context_ptr: *const Context = @ptrCast(@alignCast(data));
801 callback(info, size, context_ptr.*) catch |err| return @intFromError(err);
802 return 0;
803 }
804 }.callbackC, @ptrCast(@constCast(&context)))) {
805 0 => return,
806 else => |err| return @as(Error, @errorCast(@errorFromInt(@as(@Int(.unsigned, @bitSizeOf(anyerror)), @intCast(err))))),
807 }
808 }
809
810 var it = dl.linkmap_iterator() catch unreachable;
811
812 // The executable has no dynamic link segment, create a single entry for
813 // the whole ELF image.
814 if (it.end()) {
815 const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval;
816 const phdrs = getSelfPhdrs();
817 var info: dl_phdr_info = .{
818 .addr = for (phdrs) |phdr| switch (phdr.type) {
819 .PHDR => break @intFromPtr(phdrs.ptr) - phdr.vaddr,
820 else => {},
821 } else unreachable,
822 .name = switch (getauxval(std.elf.AT_EXECFN)) {
823 0 => "/proc/self/exe",
824 else => |name| @ptrFromInt(name),
825 },
826 .phdr = phdrs.ptr,
827 .phnum = @intCast(phdrs.len),
828 };
829
830 return callback(&info, @sizeOf(dl_phdr_info), context);
831 }
832
833 // Last return value from the callback function.
834 while (it.next()) |entry| {
835 const phdrs: []elf.ElfN.Phdr = if (entry.addr != 0) phdrs: {
836 const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.addr);
837 assert(mem.eql(u8, &ehdr.ident.magic, elf.MAGIC));
838 const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.addr + ehdr.phoff);
839 break :phdrs phdrs[0..ehdr.phnum];
840 } else getSelfPhdrs();
841
842 var info: dl_phdr_info = .{
843 .addr = entry.addr,
844 .name = entry.name,
845 .phdr = phdrs.ptr,
846 .phnum = @intCast(phdrs.len),
847 };
848
849 try callback(&info, @sizeOf(dl_phdr_info), context);
850 }
851}
852
853pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
854
855pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
856 var set: cpu_set_t = undefined;
857 switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
858 .SUCCESS => return set,
859 .FAULT => unreachable,
860 .INVAL => unreachable,
861 .SRCH => unreachable,
862 .PERM => return error.PermissionDenied,
863 else => |err| return unexpectedErrno(err),
864 }
865}
866
867pub const SigaltstackError = error{
868 /// The supplied stack size was less than MINSIGSTKSZ.
869 SizeTooSmall,
870
871 /// Attempted to change the signal stack while it was active.
872 PermissionDenied,
873} || UnexpectedError;
874
875pub fn sigaltstack(ss: ?*const stack_t, old_ss: ?*stack_t) SigaltstackError!void {
876 switch (errno(system.sigaltstack(ss, old_ss))) {
877 .SUCCESS => return,
878 .FAULT => unreachable,
879 .INVAL => unreachable,
880 .NOMEM => return error.SizeTooSmall,
881 .PERM => return error.PermissionDenied,
882 else => |err| return unexpectedErrno(err),
883 }
884}
885
886/// Return a filled sigset_t.
887pub fn sigfillset() sigset_t {
888 if (builtin.link_libc) {
889 var set: sigset_t = undefined;
890 switch (errno(system.sigfillset(&set))) {
891 .SUCCESS => return set,
892 else => unreachable,
893 }
894 }
895 return system.sigfillset();
896}
897
898/// Return an empty sigset_t.
899pub fn sigemptyset() sigset_t {
900 if (builtin.link_libc) {
901 var set: sigset_t = undefined;
902 switch (errno(system.sigemptyset(&set))) {
903 .SUCCESS => return set,
904 else => unreachable,
905 }
906 }
907 return system.sigemptyset();
908}
909
910pub fn sigaddset(set: *sigset_t, sig: SIG) void {
911 if (builtin.link_libc) {
912 switch (errno(system.sigaddset(set, sig))) {
913 .SUCCESS => return,
914 else => unreachable,
915 }
916 }
917 system.sigaddset(set, sig);
918}
919
920pub fn sigdelset(set: *sigset_t, sig: SIG) void {
921 if (builtin.link_libc) {
922 switch (errno(system.sigdelset(set, sig))) {
923 .SUCCESS => return,
924 else => unreachable,
925 }
926 }
927 system.sigdelset(set, sig);
928}
929
930pub fn sigismember(set: *const sigset_t, sig: SIG) bool {
931 if (builtin.link_libc) {
932 const rc = system.sigismember(set, sig);
933 switch (errno(rc)) {
934 .SUCCESS => return rc == 1,
935 else => unreachable,
936 }
937 }
938 return system.sigismember(set, sig);
939}
940
941/// Examine and change a signal action.
942pub fn sigaction(sig: SIG, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) void {
943 switch (errno(system.sigaction(sig, act, oact))) {
944 .SUCCESS => return,
945 // EINVAL means the signal is either invalid or some signal that cannot have its action
946 // changed. For POSIX, this means SIGKILL/SIGSTOP. For e.g. illumos, this also includes the
947 // non-standard SIGWAITING, SIGCANCEL, and SIGLWP. Either way, programmer error.
948 .INVAL => unreachable,
949 else => unreachable,
950 }
951}
952
953/// Sets the thread signal mask.
954pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void {
955 switch (errno(system.sigprocmask(@bitCast(flags), set, oldset))) {
956 .SUCCESS => return,
957 .FAULT => unreachable,
958 .INVAL => unreachable,
959 else => unreachable,
960 }
961}
962
963pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;
964
965pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
966 if (builtin.link_libc) {
967 switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
968 .SUCCESS => return mem.sliceTo(name_buffer, 0),
969 .FAULT => unreachable,
970 .NAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
971 .PERM => return error.PermissionDenied,
972 else => |err| return unexpectedErrno(err),
973 }
974 }
975 if (native_os == .linux) {
976 const uts = uname();
977 const hostname = mem.sliceTo(&uts.nodename, 0);
978 const result = name_buffer[0..hostname.len];
979 @memcpy(result, hostname);
980 return result;
981 }
982
983 @compileError("TODO implement gethostname for this OS");
984}
985
986pub fn uname() utsname {
987 var uts: utsname = undefined;
988 switch (errno(system.uname(&uts))) {
989 .SUCCESS => return uts,
990 .FAULT => unreachable,
991 else => unreachable,
992 }
993}
994
995pub const PollError = error{
996 /// The network subsystem has failed.
997 NetworkDown,
998
999 /// The kernel had no space to allocate file descriptor tables.
1000 SystemResources,
1001} || UnexpectedError;
1002
1003pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
1004 if (native_os == .windows) {
1005 @compileError("use std.Io instead");
1006 }
1007 while (true) {
1008 const fds_count = cast(nfds_t, fds.len) orelse return error.SystemResources;
1009 const rc = system.poll(fds.ptr, fds_count, timeout);
1010 switch (errno(rc)) {
1011 .SUCCESS => return @intCast(rc),
1012 .FAULT => unreachable,
1013 .INTR => continue,
1014 .INVAL => unreachable,
1015 .NOMEM => return error.SystemResources,
1016 else => |err| return unexpectedErrno(err),
1017 }
1018 }
1019 unreachable;
1020}
1021
1022pub const PPollError = error{
1023 /// The operation was interrupted by a delivery of a signal before it could complete.
1024 SignalInterrupt,
1025
1026 /// The kernel had no space to allocate file descriptor tables.
1027 SystemResources,
1028} || UnexpectedError;
1029
1030pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) PPollError!usize {
1031 var ts: timespec = undefined;
1032 var ts_ptr: ?*timespec = null;
1033 if (timeout) |timeout_ns| {
1034 ts_ptr = &ts;
1035 ts = timeout_ns.*;
1036 }
1037 const fds_count = cast(nfds_t, fds.len) orelse return error.SystemResources;
1038 const rc = system.ppoll(fds.ptr, fds_count, ts_ptr, mask);
1039 switch (errno(rc)) {
1040 .SUCCESS => return @intCast(rc),
1041 .FAULT => unreachable,
1042 .INTR => return error.SignalInterrupt,
1043 .INVAL => unreachable,
1044 .NOMEM => return error.SystemResources,
1045 else => |err| return unexpectedErrno(err),
1046 }
1047}
1048
1049pub const SetSockOptError = error{
1050 /// The socket is already connected, and a specified option cannot be set while the socket is connected.
1051 AlreadyConnected,
1052
1053 /// The option is not supported by the protocol.
1054 InvalidProtocolOption,
1055
1056 /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure.
1057 TimeoutTooBig,
1058
1059 /// Insufficient resources are available in the system to complete the call.
1060 SystemResources,
1061
1062 /// Setting the socket option requires more elevated permissions.
1063 PermissionDenied,
1064
1065 OperationUnsupported,
1066 NetworkDown,
1067 FileDescriptorNotASocket,
1068 SocketNotBound,
1069 NoDevice,
1070} || UnexpectedError;
1071
1072/// Set a socket's options.
1073pub fn setsockopt(fd: socket_t, level: i32, optname: u32, opt: []const u8) SetSockOptError!void {
1074 if (native_os == .windows) {
1075 @compileError("use std.Io instead");
1076 } else {
1077 switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(opt.len)))) {
1078 .SUCCESS => {},
1079 .BADF => unreachable, // always a race condition
1080 .NOTSOCK => unreachable, // always a race condition
1081 .INVAL => unreachable,
1082 .FAULT => unreachable,
1083 .DOM => return error.TimeoutTooBig,
1084 .ISCONN => return error.AlreadyConnected,
1085 .NOPROTOOPT => return error.InvalidProtocolOption,
1086 .NOMEM => return error.SystemResources,
1087 .NOBUFS => return error.SystemResources,
1088 .PERM => return error.PermissionDenied,
1089 .NODEV => return error.NoDevice,
1090 .OPNOTSUPP => return error.OperationUnsupported,
1091 else => |err| return unexpectedErrno(err),
1092 }
1093 }
1094}
1095
1096pub const MemFdCreateError = error{
1097 SystemFdQuotaExceeded,
1098 ProcessFdQuotaExceeded,
1099 OutOfMemory,
1100 /// Either the name provided exceeded `NAME_MAX`, or invalid flags were passed.
1101 NameTooLong,
1102 SystemOutdated,
1103} || UnexpectedError;
1104
1105pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t {
1106 switch (native_os) {
1107 .linux => {
1108 // memfd_create is available only in glibc versions starting with 2.27 and bionic versions starting with 30.
1109 const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ .major = 30, .minor = 0, .patch = 0 } else .{ .major = 2, .minor = 27, .patch = 0 });
1110 const sys = if (use_c) std.c else linux;
1111 const rc = sys.memfd_create(name, flags);
1112 switch (sys.errno(rc)) {
1113 .SUCCESS => return @intCast(rc),
1114 .FAULT => unreachable, // name has invalid memory
1115 .INVAL => return error.NameTooLong, // or, program has a bug and flags are faulty
1116 .NFILE => return error.SystemFdQuotaExceeded,
1117 .MFILE => return error.ProcessFdQuotaExceeded,
1118 .NOMEM => return error.OutOfMemory,
1119 else => |err| return unexpectedErrno(err),
1120 }
1121 },
1122 .freebsd => {
1123 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
1124 @compileError("memfd_create is unavailable on FreeBSD < 13.0");
1125 const rc = system.memfd_create(name, flags);
1126 switch (errno(rc)) {
1127 .SUCCESS => return rc,
1128 .BADF => unreachable, // name argument NULL
1129 .INVAL => unreachable, // name too long or invalid/unsupported flags.
1130 .MFILE => return error.ProcessFdQuotaExceeded,
1131 .NFILE => return error.SystemFdQuotaExceeded,
1132 .NOSYS => return error.SystemOutdated,
1133 else => |err| return unexpectedErrno(err),
1134 }
1135 },
1136 else => @compileError("target OS does not support memfd_create()"),
1137 }
1138}
1139
1140pub fn memfd_create(name: []const u8, flags: u32) MemFdCreateError!fd_t {
1141 var buffer: [NAME_MAX - "memfd:".len - 1:0]u8 = undefined;
1142 if (name.len > buffer.len) return error.NameTooLong;
1143 @memcpy(buffer[0..name.len], name);
1144 buffer[name.len] = 0;
1145 return memfd_createZ(&buffer, flags);
1146}
1147
1148pub fn getrusage(who: i32) rusage {
1149 var result: rusage = undefined;
1150 const rc = system.getrusage(who, &result);
1151 switch (errno(rc)) {
1152 .SUCCESS => return result,
1153 .INVAL => unreachable,
1154 .FAULT => unreachable,
1155 else => unreachable,
1156 }
1157}
1158
1159pub const TIOCError = error{NotATerminal};
1160
1161pub const TermiosGetError = TIOCError || UnexpectedError;
1162
1163pub fn tcgetattr(handle: fd_t) TermiosGetError!termios {
1164 while (true) {
1165 var term: termios = undefined;
1166 switch (errno(system.tcgetattr(handle, &term))) {
1167 .SUCCESS => return term,
1168 .INTR => continue,
1169 .BADF => unreachable,
1170 .NOTTY => return error.NotATerminal,
1171 else => |err| return unexpectedErrno(err),
1172 }
1173 }
1174}
1175
1176pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned};
1177
1178pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) TermiosSetError!void {
1179 while (true) {
1180 switch (errno(system.tcsetattr(handle, optional_action, &termios_p))) {
1181 .SUCCESS => return,
1182 .BADF => unreachable,
1183 .INTR => continue,
1184 .INVAL => unreachable,
1185 .NOTTY => return error.NotATerminal,
1186 .IO => return error.ProcessOrphaned,
1187 else => |err| return unexpectedErrno(err),
1188 }
1189 }
1190}
1191
1192pub const TermioGetPgrpError = TIOCError || UnexpectedError;
1193
1194/// Returns the process group ID for the TTY associated with the given handle.
1195pub fn tcgetpgrp(handle: fd_t) TermioGetPgrpError!pid_t {
1196 while (true) {
1197 var pgrp: pid_t = undefined;
1198 switch (errno(system.tcgetpgrp(handle, &pgrp))) {
1199 .SUCCESS => return pgrp,
1200 .BADF => unreachable,
1201 .INVAL => unreachable,
1202 .INTR => continue,
1203 .NOTTY => return error.NotATerminal,
1204 else => |err| return unexpectedErrno(err),
1205 }
1206 }
1207}
1208
1209pub const TermioSetPgrpError = TermioGetPgrpError || error{NotAPgrpMember};
1210
1211/// Sets the controlling process group ID for given TTY.
1212/// handle must be valid fd_t to a TTY associated with calling process.
1213/// pgrp must be a valid process group, and the calling process must be a member
1214/// of that group.
1215pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void {
1216 while (true) {
1217 switch (errno(system.tcsetpgrp(handle, &pgrp))) {
1218 .SUCCESS => return,
1219 .BADF => unreachable,
1220 .INVAL => unreachable,
1221 .INTR => continue,
1222 .NOTTY => return error.NotATerminal,
1223 .PERM => return TermioSetPgrpError.NotAPgrpMember,
1224 else => |err| return unexpectedErrno(err),
1225 }
1226 }
1227}
1228
1229pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
1230 const rc = system.signalfd(fd, mask, flags);
1231 switch (errno(rc)) {
1232 .SUCCESS => return @intCast(rc),
1233 .BADF, .INVAL => unreachable,
1234 .NFILE => return error.SystemFdQuotaExceeded,
1235 .NOMEM => return error.SystemResources,
1236 .MFILE => return error.ProcessResources,
1237 .NODEV => return error.InodeMountFail,
1238 else => |err| return unexpectedErrno(err),
1239 }
1240}
1241
1242pub const SyncError = std.Io.File.SyncError;
1243
1244/// Write all pending file contents and metadata modifications to all filesystems.
1245pub fn sync() void {
1246 system.sync();
1247}
1248
1249/// Write all pending file contents and metadata modifications to the filesystem which contains the specified file.
1250pub fn syncfs(fd: fd_t) SyncError!void {
1251 const rc = system.syncfs(fd);
1252 switch (errno(rc)) {
1253 .SUCCESS => return,
1254 .BADF, .INVAL, .ROFS => unreachable,
1255 .IO => return error.InputOutput,
1256 .NOSPC => return error.NoSpaceLeft,
1257 .DQUOT => return error.DiskQuota,
1258 else => |err| return unexpectedErrno(err),
1259 }
1260}
1261
1262/// Write all pending file contents for the specified file descriptor to the underlying filesystem, but not necessarily the metadata.
1263pub fn fdatasync(fd: fd_t) SyncError!void {
1264 const rc = system.fdatasync(fd);
1265 switch (errno(rc)) {
1266 .SUCCESS => return,
1267 .BADF, .INVAL, .ROFS => unreachable,
1268 .IO => return error.InputOutput,
1269 .NOSPC => return error.NoSpaceLeft,
1270 .DQUOT => return error.DiskQuota,
1271 else => |err| return unexpectedErrno(err),
1272 }
1273}
1274
1275pub const PrctlError = error{
1276 /// Can only occur with PR_SET_SECCOMP/SECCOMP_MODE_FILTER or
1277 /// PR_SET_MM/PR_SET_MM_EXE_FILE
1278 AccessDenied,
1279 /// Can only occur with PR_SET_MM/PR_SET_MM_EXE_FILE
1280 InvalidFileDescriptor,
1281 InvalidAddress,
1282 /// Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT,
1283 /// or PR_MPX_DISABLE_MANAGEMENT
1284 UnsupportedFeature,
1285 /// Can only occur with PR_SET_FP_MODE
1286 OperationUnsupported,
1287 PermissionDenied,
1288} || UnexpectedError;
1289
1290pub fn prctl(option: PR, args: anytype) PrctlError!u31 {
1291 if (@typeInfo(@TypeOf(args)) != .@"struct")
1292 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
1293 if (args.len > 4)
1294 @compileError("prctl takes a maximum of 4 optional arguments");
1295
1296 var buf: [4]usize = undefined;
1297 {
1298 comptime var i = 0;
1299 inline while (i < args.len) : (i += 1) buf[i] = args[i];
1300 }
1301
1302 const rc = system.prctl(@backingInt(option), buf[0], buf[1], buf[2], buf[3]);
1303 switch (errno(rc)) {
1304 .SUCCESS => return @intCast(rc),
1305 .ACCES => return error.AccessDenied,
1306 .BADF => return error.InvalidFileDescriptor,
1307 .FAULT => return error.InvalidAddress,
1308 .INVAL => unreachable,
1309 .NODEV, .NXIO => return error.UnsupportedFeature,
1310 .OPNOTSUPP => return error.OperationUnsupported,
1311 .PERM, .BUSY => return error.PermissionDenied,
1312 .RANGE => unreachable,
1313 else => |err| return unexpectedErrno(err),
1314 }
1315}
1316
1317pub const GetrlimitError = UnexpectedError;
1318
1319pub fn getrlimit(resource: rlimit_resource) GetrlimitError!rlimit {
1320 const getrlimit_sym = if (lfs64_abi) system.getrlimit64 else system.getrlimit;
1321
1322 var limits: rlimit = undefined;
1323 switch (errno(getrlimit_sym(resource, &limits))) {
1324 .SUCCESS => return limits,
1325 .FAULT => unreachable, // bogus pointer
1326 .INVAL => unreachable,
1327 else => |err| return unexpectedErrno(err),
1328 }
1329}
1330
1331pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError;
1332
1333pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void {
1334 const setrlimit_sym = if (lfs64_abi) system.setrlimit64 else system.setrlimit;
1335
1336 switch (errno(setrlimit_sym(resource, &limits))) {
1337 .SUCCESS => return,
1338 .FAULT => unreachable, // bogus pointer
1339 .INVAL => return error.LimitTooBig, // this could also mean "invalid resource", but that would be unreachable
1340 .PERM => return error.PermissionDenied,
1341 else => |err| return unexpectedErrno(err),
1342 }
1343}
1344
1345pub const MincoreError = error{
1346 /// A kernel resource was temporarily unavailable.
1347 SystemResources,
1348 /// vec points to an invalid address.
1349 InvalidAddress,
1350 /// addr is not page-aligned.
1351 InvalidSyscall,
1352 /// One of the following:
1353 /// * length is greater than user space TASK_SIZE - addr
1354 /// * addr + length contains unmapped memory
1355 OutOfMemory,
1356 /// The mincore syscall is not available on this version and configuration
1357 /// of this UNIX-like kernel.
1358 MincoreUnavailable,
1359} || UnexpectedError;
1360
1361/// Determine whether pages are resident in memory.
1362pub fn mincore(ptr: [*]align(page_size_min) u8, length: usize, vec: [*]u8) MincoreError!void {
1363 return switch (errno(system.mincore(ptr, length, vec))) {
1364 .SUCCESS => {},
1365 .AGAIN => error.SystemResources,
1366 .FAULT => error.InvalidAddress,
1367 .INVAL => error.InvalidSyscall,
1368 .NOMEM => error.OutOfMemory,
1369 .NOSYS => error.MincoreUnavailable,
1370 else => |err| unexpectedErrno(err),
1371 };
1372}
1373
1374pub const MadviseError = error{
1375 /// advice is MADV.REMOVE, but the specified address range is not a shared writable mapping.
1376 AccessDenied,
1377 /// advice is MADV.HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability.
1378 PermissionDenied,
1379 /// A kernel resource was temporarily unavailable.
1380 SystemResources,
1381 /// One of the following:
1382 /// * addr is not page-aligned or length is negative
1383 /// * advice is not valid
1384 /// * advice is MADV.DONTNEED or MADV.REMOVE and the specified address range
1385 /// includes locked, Huge TLB pages, or VM_PFNMAP pages.
1386 /// * advice is MADV.MERGEABLE or MADV.UNMERGEABLE, but the kernel was not
1387 /// configured with CONFIG_KSM.
1388 /// * advice is MADV.FREE or MADV.WIPEONFORK but the specified address range
1389 /// includes file, Huge TLB, MAP.SHARED, or VM_PFNMAP ranges.
1390 InvalidSyscall,
1391 /// (for MADV.WILLNEED) Paging in this area would exceed the process's
1392 /// maximum resident set size.
1393 WouldExceedMaximumResidentSetSize,
1394 /// One of the following:
1395 /// * (for MADV.WILLNEED) Not enough memory: paging in failed.
1396 /// * Addresses in the specified range are not currently mapped, or
1397 /// are outside the address space of the process.
1398 OutOfMemory,
1399 /// The madvise syscall is not available on this version and configuration
1400 /// of the Linux kernel.
1401 MadviseUnavailable,
1402 /// The operating system returned an undocumented error code.
1403 Unexpected,
1404};
1405
1406/// Give advice about use of memory.
1407/// This syscall is optional and is sometimes configured to be disabled.
1408pub fn madvise(ptr: [*]align(page_size_min) u8, length: usize, advice: u32) MadviseError!void {
1409 switch (errno(system.madvise(ptr, length, advice))) {
1410 .SUCCESS => return,
1411 .PERM => return error.PermissionDenied,
1412 .ACCES => return error.AccessDenied,
1413 .AGAIN => return error.SystemResources,
1414 .BADF => unreachable, // The map exists, but the area maps something that isn't a file.
1415 .INVAL => return error.InvalidSyscall,
1416 .IO => return error.WouldExceedMaximumResidentSetSize,
1417 .NOMEM => return error.OutOfMemory,
1418 .NOSYS => return error.MadviseUnavailable,
1419 else => |err| return unexpectedErrno(err),
1420 }
1421}
1422
1423pub const PerfEventOpenError = error{
1424 /// Returned if the perf_event_attr size value is too small (smaller
1425 /// than PERF_ATTR_SIZE_VER0), too big (larger than the page size),
1426 /// or larger than the kernel supports and the extra bytes are not
1427 /// zero. When E2BIG is returned, the perf_event_attr size field is
1428 /// overwritten by the kernel to be the size of the structure it was
1429 /// expecting.
1430 TooBig,
1431 /// Returned when the requested event requires CAP_SYS_ADMIN permis‐
1432 /// sions (or a more permissive perf_event paranoid setting). Some
1433 /// common cases where an unprivileged process may encounter this
1434 /// error: attaching to a process owned by a different user; moni‐
1435 /// toring all processes on a given CPU (i.e., specifying the pid
1436 /// argument as -1); and not setting exclude_kernel when the para‐
1437 /// noid setting requires it.
1438 /// Also:
1439 /// Returned on many (but not all) architectures when an unsupported
1440 /// exclude_hv, exclude_idle, exclude_user, or exclude_kernel set‐
1441 /// ting is specified.
1442 /// It can also happen, as with EACCES, when the requested event re‐
1443 /// quires CAP_SYS_ADMIN permissions (or a more permissive
1444 /// perf_event paranoid setting). This includes setting a break‐
1445 /// point on a kernel address, and (since Linux 3.13) setting a ker‐
1446 /// nel function-trace tracepoint.
1447 PermissionDenied,
1448 /// Returned if another event already has exclusive access to the
1449 /// PMU.
1450 DeviceBusy,
1451 /// Each opened event uses one file descriptor. If a large number
1452 /// of events are opened, the per-process limit on the number of
1453 /// open file descriptors will be reached, and no more events can be
1454 /// created.
1455 ProcessResources,
1456 EventRequiresUnsupportedCpuFeature,
1457 /// Returned if you try to add more breakpoint
1458 /// events than supported by the hardware.
1459 TooManyBreakpoints,
1460 /// Returned if PERF_SAMPLE_STACK_USER is set in sample_type and it
1461 /// is not supported by hardware.
1462 SampleStackNotSupported,
1463 /// Returned if an event requiring a specific hardware feature is
1464 /// requested but there is no hardware support. This includes re‐
1465 /// questing low-skid events if not supported, branch tracing if it
1466 /// is not available, sampling if no PMU interrupt is available, and
1467 /// branch stacks for software events.
1468 EventNotSupported,
1469 /// Returned if PERF_SAMPLE_CALLCHAIN is requested and sam‐
1470 /// ple_max_stack is larger than the maximum specified in
1471 /// /proc/sys/kernel/perf_event_max_stack.
1472 SampleMaxStackOverflow,
1473 /// Returned if attempting to attach to a process that does not exist.
1474 ProcessNotFound,
1475} || UnexpectedError;
1476
1477pub fn perf_event_open(
1478 attr: *system.perf_event_attr,
1479 pid: pid_t,
1480 cpu: i32,
1481 group_fd: fd_t,
1482 flags: usize,
1483) PerfEventOpenError!fd_t {
1484 if (native_os == .linux) {
1485 // There is no syscall wrapper for this function exposed by libcs
1486 const rc = linux.perf_event_open(attr, pid, cpu, group_fd, flags);
1487 switch (linux.errno(rc)) {
1488 .SUCCESS => return @intCast(rc),
1489 .@"2BIG" => return error.TooBig,
1490 .ACCES => return error.PermissionDenied,
1491 .BADF => unreachable, // group_fd file descriptor is not valid.
1492 .BUSY => return error.DeviceBusy,
1493 .FAULT => unreachable, // Segmentation fault.
1494 .INVAL => unreachable, // Bad attr settings.
1495 .INTR => unreachable, // Mixed perf and ftrace handling for a uprobe.
1496 .MFILE => return error.ProcessResources,
1497 .NODEV => return error.EventRequiresUnsupportedCpuFeature,
1498 .NOENT => unreachable, // Invalid type setting.
1499 .NOSPC => return error.TooManyBreakpoints,
1500 .NOSYS => return error.SampleStackNotSupported,
1501 .OPNOTSUPP => return error.EventNotSupported,
1502 .OVERFLOW => return error.SampleMaxStackOverflow,
1503 .PERM => return error.PermissionDenied,
1504 .SRCH => return error.ProcessNotFound,
1505 else => |err| return unexpectedErrno(err),
1506 }
1507 }
1508}
1509
1510pub const PtraceError = error{
1511 DeadLock,
1512 DeviceBusy,
1513 InputOutput,
1514 NameTooLong,
1515 OperationUnsupported,
1516 OutOfMemory,
1517 ProcessNotFound,
1518 PermissionDenied,
1519} || UnexpectedError;
1520
1521pub fn ptrace(request: u32, pid: pid_t, addr: usize, data: usize) PtraceError!void {
1522 return switch (native_os) {
1523 .windows,
1524 .wasi,
1525 .emscripten,
1526 .haiku,
1527 .illumos,
1528 .plan9,
1529 => @compileError("ptrace unsupported by target OS"),
1530
1531 .linux => switch (errno(if (builtin.link_libc) std.c.ptrace(
1532 @intCast(request),
1533 pid,
1534 @ptrFromInt(addr),
1535 @ptrFromInt(data),
1536 ) else linux.ptrace(request, pid, addr, data, 0))) {
1537 .SUCCESS => {},
1538 .SRCH => error.ProcessNotFound,
1539 .FAULT => unreachable,
1540 .INVAL => unreachable,
1541 .IO => return error.InputOutput,
1542 .PERM => error.PermissionDenied,
1543 .BUSY => error.DeviceBusy,
1544 else => |err| return unexpectedErrno(err),
1545 },
1546
1547 .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (errno(std.c.ptrace(
1548 @fromBackingInt(@intCast(request)),
1549 pid,
1550 @ptrFromInt(addr),
1551 @intCast(data),
1552 ))) {
1553 .SUCCESS => {},
1554 .SRCH => error.ProcessNotFound,
1555 .INVAL => unreachable,
1556 .PERM => error.PermissionDenied,
1557 .BUSY => error.DeviceBusy,
1558 else => |err| return unexpectedErrno(err),
1559 },
1560
1561 .dragonfly => switch (errno(std.c.ptrace(
1562 @intCast(request),
1563 pid,
1564 @ptrFromInt(addr),
1565 @intCast(data),
1566 ))) {
1567 .SUCCESS => {},
1568 .SRCH => error.ProcessNotFound,
1569 .INVAL => unreachable,
1570 .PERM => error.PermissionDenied,
1571 .BUSY => error.DeviceBusy,
1572 else => |err| return unexpectedErrno(err),
1573 },
1574
1575 .freebsd => switch (errno(std.c.ptrace(
1576 @intCast(request),
1577 pid,
1578 @ptrFromInt(addr),
1579 @intCast(data),
1580 ))) {
1581 .SUCCESS => {},
1582 .SRCH => error.ProcessNotFound,
1583 .INVAL => unreachable,
1584 .PERM => error.PermissionDenied,
1585 .BUSY => error.DeviceBusy,
1586 .NOENT, .NOMEM => error.OutOfMemory,
1587 .NAMETOOLONG => error.NameTooLong,
1588 else => |err| return unexpectedErrno(err),
1589 },
1590
1591 .netbsd => switch (errno(std.c.ptrace(
1592 @intCast(request),
1593 pid,
1594 @ptrFromInt(addr),
1595 @intCast(data),
1596 ))) {
1597 .SUCCESS => {},
1598 .SRCH => error.ProcessNotFound,
1599 .INVAL => unreachable,
1600 .PERM => error.PermissionDenied,
1601 .BUSY => error.DeviceBusy,
1602 .DEADLK => error.DeadLock,
1603 else => |err| return unexpectedErrno(err),
1604 },
1605
1606 .openbsd => switch (errno(std.c.ptrace(
1607 @intCast(request),
1608 pid,
1609 @ptrFromInt(addr),
1610 @intCast(data),
1611 ))) {
1612 .SUCCESS => {},
1613 .SRCH => error.ProcessNotFound,
1614 .INVAL => unreachable,
1615 .PERM => error.PermissionDenied,
1616 .BUSY => error.DeviceBusy,
1617 .NOTSUP => error.OperationUnsupported,
1618 else => |err| return unexpectedErrno(err),
1619 },
1620
1621 else => @compileError("std.posix.ptrace unimplemented for target OS"),
1622 };
1623}
1624
1625pub const NameToFileHandleAtError = error{
1626 FileNotFound,
1627 NotDir,
1628 OperationUnsupported,
1629 NameTooLong,
1630 Unexpected,
1631};
1632
1633pub fn name_to_handle_at(
1634 dirfd: fd_t,
1635 pathname: []const u8,
1636 handle: *std.os.linux.file_handle,
1637 mount_id: *i32,
1638 flags: u32,
1639) NameToFileHandleAtError!void {
1640 const pathname_c = try toPosixPath(pathname);
1641 return name_to_handle_atZ(dirfd, &pathname_c, handle, mount_id, flags);
1642}
1643
1644pub fn name_to_handle_atZ(
1645 dirfd: fd_t,
1646 pathname_z: [*:0]const u8,
1647 handle: *std.os.linux.file_handle,
1648 mount_id: *i32,
1649 flags: u32,
1650) NameToFileHandleAtError!void {
1651 switch (errno(system.name_to_handle_at(dirfd, pathname_z, handle, mount_id, flags))) {
1652 .SUCCESS => {},
1653 .FAULT => unreachable, // pathname, mount_id, or handle outside accessible address space
1654 .INVAL => unreachable, // bad flags, or handle_bytes too big
1655 .NOENT => return error.FileNotFound,
1656 .NOTDIR => return error.NotDir,
1657 .OPNOTSUPP => return error.OperationUnsupported,
1658 .OVERFLOW => return error.NameTooLong,
1659 else => |err| return unexpectedErrno(err),
1660 }
1661}
1662
1663pub const lfs64_abi = native_os == .linux and builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid());
1664
1665pub const UnexpectedError = std.Io.UnexpectedError;
1666
1667/// Call this when you made a syscall or something that sets errno
1668/// and you get an unexpected error.
1669pub fn unexpectedErrno(err: E) UnexpectedError {
1670 if (std.options.unexpected_error_tracing) {
1671 std.debug.print("unexpected errno: {d}\n", .{@backingInt(err)});
1672 std.debug.dumpCurrentStackTrace(.{});
1673 }
1674 return error.Unexpected;
1675}
1676
1677/// Used to convert a slice to a null terminated slice on the stack.
1678pub fn toPosixPath(file_path: []const u8) error{NameTooLong}![PATH_MAX - 1:0]u8 {
1679 if (std.debug.runtime_safety) assert(mem.findScalar(u8, file_path, 0) == null);
1680 var path_with_null: [PATH_MAX - 1:0]u8 = undefined;
1681 // >= rather than > to make room for the null byte
1682 if (file_path.len >= PATH_MAX) return error.NameTooLong;
1683 @memcpy(path_with_null[0..file_path.len], file_path);
1684 path_with_null[file_path.len] = 0;
1685 return path_with_null;
1686}