| 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. |
| 10 | const builtin = @import("builtin"); |
| 11 | const native_os = builtin.os.tag; |
| 12 | |
| 13 | const std = @import("std.zig"); |
| 14 | const Io = std.Io; |
| 15 | const mem = std.mem; |
| 16 | const maxInt = std.math.maxInt; |
| 17 | const cast = std.math.cast; |
| 18 | const assert = std.debug.assert; |
| 19 | const page_size_min = std.heap.page_size_min; |
| 20 | |
| 21 | test { |
| 22 | _ = @import("posix/test.zig"); |
| 23 | } |
| 24 | |
| 25 | /// Whether to use libc for the POSIX API layer. |
| 26 | const use_libc = builtin.link_libc or switch (native_os) { |
| 27 | .windows, .wasi => true, |
| 28 | else => false, |
| 29 | }; |
| 30 | |
| 31 | const linux = std.os.linux; |
| 32 | const windows = std.os.windows; |
| 33 | const wasi = std.os.wasi; |
| 34 | |
| 35 | /// A libc-compatible API layer. |
| 36 | pub const system = if (use_libc) |
| 37 | std.c |
| 38 | else 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 | |
| 72 | pub const AF = system.AF; |
| 73 | pub const AF_SUN = system.AF_SUN; |
| 74 | pub const AI = system.AI; |
| 75 | pub const ARCH = system.ARCH; |
| 76 | pub const AT = system.AT; |
| 77 | pub const AT_SUN = system.AT_SUN; |
| 78 | pub const CLOCK = system.CLOCK; |
| 79 | pub const CPU_COUNT = system.CPU_COUNT; |
| 80 | pub const CTL = system.CTL; |
| 81 | pub const DT = system.DT; |
| 82 | pub const E = system.E; |
| 83 | pub const Elf_Symndx = system.Elf_Symndx; |
| 84 | pub const F = system.F; |
| 85 | pub const FD_CLOEXEC = system.FD_CLOEXEC; |
| 86 | pub const Flock = system.Flock; |
| 87 | pub const HOST_NAME_MAX = system.HOST_NAME_MAX; |
| 88 | pub const HW = system.HW; |
| 89 | pub const IFNAMESIZE = system.IFNAMESIZE; |
| 90 | pub const IOV_MAX = system.IOV_MAX; |
| 91 | pub const IP = system.IP; |
| 92 | pub const IPV6 = system.IPV6; |
| 93 | pub const IPPROTO = system.IPPROTO; |
| 94 | pub const IPTOS = system.IPTOS; |
| 95 | pub const KERN = system.KERN; |
| 96 | pub const Kevent = system.Kevent; |
| 97 | pub const MADV = system.MADV; |
| 98 | pub const MAP = system.MAP; |
| 99 | pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN; |
| 100 | pub const MCL = system.MCL; |
| 101 | pub const MFD = system.MFD; |
| 102 | pub const MLOCK = system.MLOCK; |
| 103 | pub const MREMAP = system.MREMAP; |
| 104 | pub const MSF = system.MSF; |
| 105 | pub const MSG = system.MSG; |
| 106 | pub const NAME_MAX = system.NAME_MAX; |
| 107 | pub const NSIG = system.NSIG; |
| 108 | pub const O = system.O; |
| 109 | pub const PATH_MAX = system.PATH_MAX; |
| 110 | pub const POLL = system.POLL; |
| 111 | pub const POSIX_FADV = system.POSIX_FADV; |
| 112 | pub const PR = system.PR; |
| 113 | pub const PROT = system.PROT; |
| 114 | pub const RLIM = system.RLIM; |
| 115 | pub const S = system.S; |
| 116 | pub const SA = system.SA; |
| 117 | pub const SC = system.SC; |
| 118 | pub const SCM = system.SCM; |
| 119 | pub const SEEK = system.SEEK; |
| 120 | pub const SHUT = system.SHUT; |
| 121 | pub const SIG = system.SIG; |
| 122 | pub const SIOCGIFINDEX = system.SIOCGIFINDEX; |
| 123 | pub const SO = system.SO; |
| 124 | pub const SOCK = system.SOCK; |
| 125 | pub const SOL = system.SOL; |
| 126 | pub const IFF = system.IFF; |
| 127 | pub const STDERR_FILENO = system.STDERR_FILENO; |
| 128 | pub const STDIN_FILENO = system.STDIN_FILENO; |
| 129 | pub const STDOUT_FILENO = system.STDOUT_FILENO; |
| 130 | pub const SYS = system.SYS; |
| 131 | pub 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. |
| 138 | pub const Stat = switch (native_os) { |
| 139 | .windows => void, |
| 140 | .linux => void, |
| 141 | else => system.Stat, |
| 142 | }; |
| 143 | pub const T = system.T; |
| 144 | pub const TCP = system.TCP; |
| 145 | pub const VDSO = system.VDSO; |
| 146 | pub const W = system.W; |
| 147 | pub const _SC = system._SC; |
| 148 | pub const addrinfo = system.addrinfo; |
| 149 | pub const blkcnt_t = system.blkcnt_t; |
| 150 | pub const blksize_t = system.blksize_t; |
| 151 | pub const clock_t = system.clock_t; |
| 152 | pub const clockid_t = system.clockid_t; |
| 153 | pub const timerfd_clockid_t = system.timerfd_clockid_t; |
| 154 | pub const cpu_set_t = system.cpu_set_t; |
| 155 | pub const dev_t = system.dev_t; |
| 156 | pub const dl_phdr_info = system.dl_phdr_info; |
| 157 | pub const fd_t = system.fd_t; |
| 158 | pub const file_obj = system.file_obj; |
| 159 | pub const gid_t = system.gid_t; |
| 160 | pub const ifreq = system.ifreq; |
| 161 | pub const in_pktinfo = system.in_pktinfo; |
| 162 | pub const in6_pktinfo = system.in6_pktinfo; |
| 163 | pub const ino_t = system.ino_t; |
| 164 | pub const linger = system.linger; |
| 165 | pub const mode_t = system.mode_t; |
| 166 | pub const msghdr = system.msghdr; |
| 167 | pub const msghdr_const = system.msghdr_const; |
| 168 | pub const nfds_t = system.nfds_t; |
| 169 | pub const nlink_t = system.nlink_t; |
| 170 | pub const off_t = system.off_t; |
| 171 | pub const pid_t = system.pid_t; |
| 172 | pub const pollfd = system.pollfd; |
| 173 | pub const port_event = system.port_event; |
| 174 | pub const port_notify = system.port_notify; |
| 175 | pub const port_t = system.port_t; |
| 176 | pub const rlim_t = system.rlim_t; |
| 177 | pub const rlimit = system.rlimit; |
| 178 | pub const rlimit_resource = system.rlimit_resource; |
| 179 | pub const rusage = system.rusage; |
| 180 | pub const sa_family_t = system.sa_family_t; |
| 181 | pub const siginfo_t = system.siginfo_t; |
| 182 | pub const sigset_t = system.sigset_t; |
| 183 | pub const sigrtmin = system.sigrtmin; |
| 184 | pub const sigrtmax = system.sigrtmax; |
| 185 | pub const sockaddr = system.sockaddr; |
| 186 | pub const socklen_t = system.socklen_t; |
| 187 | pub const stack_t = system.stack_t; |
| 188 | pub const time_t = system.time_t; |
| 189 | pub const timespec = system.timespec; |
| 190 | pub const timestamp_t = system.timestamp_t; |
| 191 | pub const timeval = system.timeval; |
| 192 | pub const timezone = system.timezone; |
| 193 | pub const UTIME = system.UTIME; |
| 194 | pub const uid_t = system.uid_t; |
| 195 | pub const user_desc = system.user_desc; |
| 196 | pub const utsname = system.utsname; |
| 197 | |
| 198 | pub const termios = system.termios; |
| 199 | pub const CSIZE = system.CSIZE; |
| 200 | pub const NCCS = system.NCCS; |
| 201 | pub const cc_t = system.cc_t; |
| 202 | pub const V = system.V; |
| 203 | pub const speed_t = system.speed_t; |
| 204 | pub const tc_iflag_t = system.tc_iflag_t; |
| 205 | pub const tc_oflag_t = system.tc_oflag_t; |
| 206 | pub const tc_cflag_t = system.tc_cflag_t; |
| 207 | pub const tc_lflag_t = system.tc_lflag_t; |
| 208 | |
| 209 | pub const F_OK = system.F_OK; |
| 210 | pub const R_OK = system.R_OK; |
| 211 | pub const W_OK = system.W_OK; |
| 212 | pub const X_OK = system.X_OK; |
| 213 | |
| 214 | pub const iovec = extern struct { |
| 215 | base: [*]u8, |
| 216 | len: usize, |
| 217 | }; |
| 218 | |
| 219 | pub const iovec_const = extern struct { |
| 220 | base: [*]const u8, |
| 221 | len: usize, |
| 222 | }; |
| 223 | |
| 224 | pub 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 | |
| 248 | pub const TCSA = enum(c_uint) { |
| 249 | NOW, |
| 250 | DRAIN, |
| 251 | FLUSH, |
| 252 | _, |
| 253 | }; |
| 254 | |
| 255 | pub const winsize = extern struct { |
| 256 | row: u16, |
| 257 | col: u16, |
| 258 | xpixel: u16, |
| 259 | ypixel: u16, |
| 260 | }; |
| 261 | |
| 262 | pub 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 | |
| 269 | pub 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 | |
| 288 | pub 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. |
| 296 | pub const errno = system.errno; |
| 297 | |
| 298 | pub const RebootError = error{ |
| 299 | PermissionDenied, |
| 300 | } || UnexpectedError; |
| 301 | |
| 302 | pub 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 | |
| 316 | pub 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 | |
| 348 | pub const RaiseError = UnexpectedError; |
| 349 | |
| 350 | pub 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 | |
| 376 | pub const KillError = error{ ProcessNotFound, PermissionDenied } || UnexpectedError; |
| 377 | |
| 378 | pub 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 | |
| 388 | pub 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)`. |
| 400 | pub 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 | |
| 433 | pub 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`. |
| 441 | pub 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`. |
| 457 | pub 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 | |
| 501 | pub fn getppid() pid_t { |
| 502 | return system.getppid(); |
| 503 | } |
| 504 | |
| 505 | pub 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 | |
| 521 | pub 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 | |
| 540 | pub 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 | |
| 550 | pub 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 | |
| 563 | pub 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 | |
| 577 | pub 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 | |
| 592 | pub 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 | |
| 618 | pub 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 |
| 642 | pub 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. |
| 684 | pub 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 | |
| 695 | pub 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 | |
| 703 | pub 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 | |
| 729 | pub const MSyncError = error{ |
| 730 | UnmappedMemory, |
| 731 | PermissionDenied, |
| 732 | } || UnexpectedError; |
| 733 | |
| 734 | pub 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 | |
| 744 | pub const SysCtlError = error{ |
| 745 | PermissionDenied, |
| 746 | SystemResources, |
| 747 | NameTooLong, |
| 748 | UnknownName, |
| 749 | } || UnexpectedError; |
| 750 | |
| 751 | pub 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 | |
| 776 | pub 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 | |
| 783 | pub 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 | |
| 853 | pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError; |
| 854 | |
| 855 | pub 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 | |
| 867 | pub 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 | |
| 875 | pub 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. |
| 887 | pub 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. |
| 899 | pub 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 | |
| 910 | pub 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 | |
| 920 | pub 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 | |
| 930 | pub 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. |
| 942 | pub 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. |
| 954 | pub 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 | |
| 963 | pub const GetHostNameError = error{PermissionDenied} || UnexpectedError; |
| 964 | |
| 965 | pub 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 | |
| 986 | pub 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 | |
| 995 | pub 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 | |
| 1003 | pub 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 | |
| 1022 | pub 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 | |
| 1030 | pub 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 | |
| 1049 | pub 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. |
| 1073 | pub 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 | |
| 1096 | pub 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 | |
| 1105 | pub 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 | |
| 1140 | pub 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 | |
| 1148 | pub 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 | |
| 1159 | pub const TIOCError = error{NotATerminal}; |
| 1160 | |
| 1161 | pub const TermiosGetError = TIOCError || UnexpectedError; |
| 1162 | |
| 1163 | pub 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 | |
| 1176 | pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned}; |
| 1177 | |
| 1178 | pub 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 | |
| 1192 | pub const TermioGetPgrpError = TIOCError || UnexpectedError; |
| 1193 | |
| 1194 | /// Returns the process group ID for the TTY associated with the given handle. |
| 1195 | pub 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 | |
| 1209 | pub 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. |
| 1215 | pub 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 | |
| 1229 | pub 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 | |
| 1242 | pub const SyncError = std.Io.File.SyncError; |
| 1243 | |
| 1244 | /// Write all pending file contents and metadata modifications to all filesystems. |
| 1245 | pub 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. |
| 1250 | pub 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. |
| 1263 | pub 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 | |
| 1275 | pub 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 | |
| 1290 | pub 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 | |
| 1317 | pub const GetrlimitError = UnexpectedError; |
| 1318 | |
| 1319 | pub 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 | |
| 1331 | pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError; |
| 1332 | |
| 1333 | pub 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 | |
| 1345 | pub 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. |
| 1362 | pub 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 | |
| 1374 | pub 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. |
| 1408 | pub 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 | |
| 1423 | pub 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 | |
| 1477 | pub 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 | |
| 1510 | pub const PtraceError = error{ |
| 1511 | DeadLock, |
| 1512 | DeviceBusy, |
| 1513 | InputOutput, |
| 1514 | NameTooLong, |
| 1515 | OperationUnsupported, |
| 1516 | OutOfMemory, |
| 1517 | ProcessNotFound, |
| 1518 | PermissionDenied, |
| 1519 | } || UnexpectedError; |
| 1520 | |
| 1521 | pub 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 | |
| 1625 | pub const NameToFileHandleAtError = error{ |
| 1626 | FileNotFound, |
| 1627 | NotDir, |
| 1628 | OperationUnsupported, |
| 1629 | NameTooLong, |
| 1630 | Unexpected, |
| 1631 | }; |
| 1632 | |
| 1633 | pub 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 | |
| 1644 | pub 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 | |
| 1663 | pub const lfs64_abi = native_os == .linux and builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid()); |
| 1664 | |
| 1665 | pub 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. |
| 1669 | pub 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. |
| 1678 | pub 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 | } |